1 #!/usr/bin/env python
\r
2 # -*- coding: utf-8 -*-
\r
4 #Pydun.py - mapping tool
\r
5 #copyright (c) 2013 WATAHIKI Hiroyuki
\r
6 #url: http://sourceforge.jp/projects/pydun/
\r
7 #email: hrwatahiki at gmail.com
\r
15 import xml.etree.ElementTree
\r
17 from PySide import QtCore, QtGui
\r
25 projecturl = "http://sourceforge.jp/projects/pydun/"
\r
26 projectrssurl = "http://sourceforge.jp/projects/pydun/releases/rss"
\r
27 projectversion = "1.0.5"
\r
30 class MainWindow(QtGui.QMainWindow):
\r
32 def __init__(self, parent=None):
\r
36 super(MainWindow, self).__init__(parent)
\r
38 _undomanager = UndoManager()
\r
39 _mapimages = MapImages()
\r
41 _undomanager.changed.connect(self.updateundostate)
\r
44 if len(sys.argv) >= 2:
\r
45 self.open(unicode(sys.argv[1], locale.getpreferredencoding()))
\r
47 self.mainframe = MainFrame(self)
\r
48 self.setCentralWidget(self.mainframe)
\r
50 self.statusbar = QtGui.QStatusBar(self)
\r
51 self.statusbar.showMessage(u"")
\r
52 self.setStatusBar(self.statusbar)
\r
53 if "windowSize" in config:
\r
56 config["windowSize"]["width"],
\r
57 config["windowSize"]["height"]))
\r
61 filemenu = self.menuBar().addMenu(u"ファイル(&F)")
\r
63 newact = QtGui.QAction(u"新規(&N)", self)
\r
64 newact.triggered.connect(self.new_triggered)
\r
65 newact.setShortcut(QtGui.QKeySequence.New)
\r
66 filemenu.addAction(newact)
\r
68 openact = QtGui.QAction(u"開く(&O)...", self)
\r
69 openact.triggered.connect(self.open_triggered)
\r
70 openact.setShortcut(QtGui.QKeySequence.Open)
\r
71 filemenu.addAction(openact)
\r
73 saveact = QtGui.QAction(u"上書き保存(&S)", self)
\r
74 saveact.triggered.connect(self.save_triggered)
\r
75 saveact.setShortcut(QtGui.QKeySequence.Save)
\r
76 filemenu.addAction(saveact)
\r
78 saveasact = QtGui.QAction(u"名前をつけて保存(&A)...", self)
\r
79 saveasact.triggered.connect(self.saveas_triggered)
\r
80 saveasact.setShortcut(QtGui.QKeySequence.SaveAs)
\r
81 filemenu.addAction(saveasact)
\r
83 exitact = QtGui.QAction(u"終了(&E)", self)
\r
84 exitact.triggered.connect(self.exit_triggered)
\r
85 exitact.setShortcut(QtGui.QKeySequence.Quit)
\r
86 filemenu.addAction(exitact)
\r
89 editmenu = self.menuBar().addMenu(u"編集(&E)")
\r
90 self.undoact = QtGui.QAction(u"元に戻す(&U)", self)
\r
91 self.undoact.triggered.connect(self.undo_triggered)
\r
92 self.undoact.setShortcut(QtGui.QKeySequence.Undo)
\r
93 editmenu.addAction(self.undoact)
\r
94 self.redoact = QtGui.QAction(u"やり直し(&R)", self)
\r
95 self.redoact.triggered.connect(self.redo_triggered)
\r
96 self.redoact.setShortcut(QtGui.QKeySequence.Redo)
\r
97 editmenu.addAction(self.redoact)
\r
98 editmenu.addSeparator()
\r
99 setmapsizeact = QtGui.QAction(u"マップのサイズ(&S)", self)
\r
100 setmapsizeact.triggered.connect(self.setmapsize_triggered)
\r
101 editmenu.addAction(setmapsizeact)
\r
102 setorigineact = QtGui.QAction(u"座標設定(&O)", self)
\r
103 setorigineact.triggered.connect(self.setorigine_triggered)
\r
104 editmenu.addAction(setorigineact)
\r
107 helpmenu = self.menuBar().addMenu(u"ヘルプ(&H)")
\r
108 tutorialact = QtGui.QAction(u"ヘルプの表示(&H)", self)
\r
109 tutorialact.triggered.connect(self.tutorial_triggered)
\r
110 tutorialact.setShortcut(QtGui.QKeySequence.HelpContents)
\r
111 helpmenu.addAction(tutorialact)
\r
112 projectact = QtGui.QAction(u"プロジェクトのWebサイト(&W)", self)
\r
113 projectact.triggered.connect(self.project_triggered)
\r
114 helpmenu.addAction(projectact)
\r
115 aboutact = QtGui.QAction(u"Pydunについて(&A)...", self)
\r
116 aboutact.triggered.connect(self.about_triggered)
\r
117 helpmenu.addAction(aboutact)
\r
119 @QtCore.Slot(bool, bool)
\r
120 def updateundostate(self, canundo, canredo):
\r
122 self.undoact.setEnabled(True)
\r
124 self.undoact.setDisabled(True)
\r
126 self.redoact.setEnabled(True)
\r
128 self.redoact.setDisabled(True)
\r
130 def setTitle(self, filename):
\r
131 s ="Pydun - " + self.getfilename(filename)
\r
132 self.setWindowTitle(s)
\r
134 def getfilename(self, filename):
\r
135 if filename == None:
\r
138 s = os.path.basename(filename)
\r
142 def new_triggered(self):
\r
143 if self.confirmdiscarding():
\r
150 _mapengine = MapEngine(20, 20, 1, -1, 0, +19)
\r
151 _undomanager.init(_mapengine.savestring())
\r
152 self.setTitle(None)
\r
154 self.mainframe.mapframe.repaint()
\r
158 def confirmdiscarding(self):
\r
159 if not _undomanager.commited:
\r
160 dlg = PydunAskSaveDialog(self, self.getfilename(_mapengine.filename))
\r
162 if ret == QtGui.QMessageBox.Cancel:
\r
164 elif ret == QtGui.QMessageBox.Save:
\r
165 saved = self.save_triggered()
\r
171 def open_triggered(self):
\r
172 if self.confirmdiscarding():
\r
175 d = os.path.dirname(_mapengine.filename)
\r
178 filename = QtGui.QFileDialog.getOpenFileName(
\r
180 filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")
\r
181 if filename[0] != u"":
\r
182 self.open(filename[0])
\r
184 def open(self, filename):
\r
185 _mapengine.load(filename)
\r
186 _undomanager.init(_mapengine.savestring())
\r
187 self.setTitle(_mapengine.filename)
\r
189 self.mainframe.mapframe.repaint()
\r
194 def save_triggered(self):
\r
195 if _mapengine.filename:
\r
196 self.save(_mapengine.filename)
\r
199 saved = self.saveas_triggered()
\r
203 def saveas_triggered(self):
\r
206 d = os.path.dirname(_mapengine.filename)
\r
209 filename = QtGui.QFileDialog.getSaveFileName(
\r
211 filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")
\r
212 if filename[0] != u"":
\r
213 self.save(filename[0])
\r
218 def save(self, filename):
\r
219 _mapengine.save(filename)
\r
220 _undomanager.commit()
\r
221 self.setTitle(_mapengine.filename)
\r
224 def exit_triggered(self):
\r
227 def closeEvent(self, event):
\r
235 global configfilename
\r
236 if self.confirmdiscarding():
\r
237 config["windowSize"] = dict()
\r
238 config["windowSize"]["width"] = self.size().width()
\r
239 config["windowSize"]["height"] = self.size().height()
\r
240 with open(configfilename, "w") as f:
\r
241 yaml.safe_dump(config, f, default_flow_style=False)
\r
247 def undo_triggered(self):
\r
249 _mapengine.loadfromstring(_undomanager.undo())
\r
250 self.mainframe.mapframe.repaint()
\r
253 def redo_triggered(self):
\r
255 _mapengine.loadfromstring(_undomanager.redo())
\r
256 self.mainframe.mapframe.repaint()
\r
259 def setorigine_triggered(self):
\r
261 if self.mainframe.mapframe.setoriginemode:
\r
262 QtGui.QMessageBox.information(
\r
263 self, title, u"座標設定を中止します。", QtGui.QMessageBox.Ok)
\r
264 self.mainframe.mapframe.setoriginemode = False
\r
266 if QtGui.QMessageBox.Ok == QtGui.QMessageBox.information(
\r
267 self, title, u"基準にする地点をクリックしてください。",
\r
268 (QtGui.QMessageBox.Ok| QtGui.QMessageBox.Cancel)):
\r
269 self.mainframe.mapframe.setoriginemode = True
\r
272 def setmapsize_triggered(self):
\r
273 dlg = SetSizeDialog(self)
\r
274 dlg.setoriginalsize(_mapengine.width, _mapengine.height)
\r
276 if dlg.result() == QtGui.QDialog.Accepted:
\r
277 top, bottom, left, right = dlg.getsize()
\r
278 _mapengine.changesize(top, bottom, left, right)
\r
279 _undomanager.save(_mapengine.savestring())
\r
280 self.mainframe.mapframe.repaint()
\r
283 def tutorial_triggered(self):
\r
284 url = basedir() + "/help/index.html"
\r
285 webbrowser.open_new_tab(url)
\r
288 def project_triggered(self):
\r
289 webbrowser.open_new_tab(projecturl)
\r
292 def about_triggered(self):
\r
293 QtGui.QMessageBox.about(self, "Pydun",
\r
294 u"<h1>Pydun.py "+ projectversion + "</h1>"
\r
295 u"<p>Copyright (c) 2013 WATAHIKI Hiroyuki</p>"
\r
296 u"<p>url: <a href='" + projecturl + "'>" + projecturl + "</a></p>"
\r
297 u"<p>e-mail: hrwatahiki at gmail.com</p>"
\r
298 u"<p>このソフトウェアはMITライセンスです。</p>"
\r
299 u"<p>このソフトウェアは以下のソフトウェアを使用しています。: "
\r
300 u"Python, PySide, PyYAML "
\r
301 u"これらの作成者に深く感謝いたします。</p>"
\r
302 u"<p>詳細はLICENCE.txtを参照してください。</p>")
\r
305 class MainFrame(QtGui.QFrame):
\r
306 create_wall_menu_triggered_signal = QtCore.Signal(int, int, str, int)
\r
308 def __init__(self, parent=None):
\r
309 super(MainFrame, self).__init__(parent)
\r
311 self.mapframe = MapFrame(self)
\r
312 scrollarea = QtGui.QScrollArea(self)
\r
313 scrollarea.setWidget(self.mapframe)
\r
315 self.detail = QtGui.QLabel(self)
\r
316 self.detail.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
317 self.detail.setText(u"")
\r
318 self.detail.setMaximumHeight(100)
\r
319 self.detail.setMinimumHeight(100)
\r
321 self.boxdrawbutton = QtGui.QRadioButton(self)
\r
322 self.boxdrawbutton.setText(u"ボックス形式で壁を描画(&B)")
\r
323 self.boxdrawbutton.setChecked(True)
\r
324 self.boxdrawbutton.setSizePolicy(
\r
325 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
327 self.growdrawbutton = QtGui.QRadioButton(self)
\r
328 self.growdrawbutton.setText(u"足跡形式で壁を描画(&G)")
\r
329 self.growdrawbutton.setChecked(False)
\r
330 self.growdrawbutton.setSizePolicy(
\r
331 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
333 self.backcolorbutton = QtGui.QRadioButton(self)
\r
334 self.backcolorbutton.setText(u"背景色(&C)")
\r
335 self.backcolorbutton.setChecked(False)
\r
336 self.backcolorbutton.setSizePolicy(
\r
337 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
339 self.setbackcolorbutton = QtGui.QPushButton(self)
\r
340 self.setbackcolorbutton.setText(u"背景色を設定(&S)...")
\r
341 self.setbackcolorbutton.setSizePolicy(
\r
342 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
344 self.backcolorbox = ColorBox(self)
\r
345 self.backcolorbox.setMinimumSize(30, 30)
\r
346 self.backcolorbox.setSizePolicy(
\r
347 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
349 latestversion = getlatestversion()
\r
350 if latestversion != projectversion:
\r
351 self.update = QtGui.QLabel(self)
\r
352 self.update.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
353 self.update.setText(u"<a href='{url}'>最新のPydun({ver})がダウンロードできます。</a>".format(url=projecturl, ver=latestversion))
\r
354 self.update.setOpenExternalLinks(True)
\r
356 layout = QtGui.QGridLayout(self)
\r
357 layout.addWidget(scrollarea, 0, 0, 1, 3)
\r
358 layout.addWidget(self.detail, 1, 0, 4, 1)
\r
359 layout.addWidget(self.boxdrawbutton, 1, 1, 1, 2)
\r
360 layout.addWidget(self.growdrawbutton, 2, 1, 1, 2)
\r
361 layout.addWidget(self.backcolorbutton, 3, 1, 1, 2)
\r
362 layout.addWidget(self.setbackcolorbutton, 4, 1, 1, 1)
\r
363 layout.addWidget(self.backcolorbox, 4, 2, 1, 1)
\r
364 if latestversion != projectversion:
\r
365 layout.addWidget(self.update, 5, 0, 1, 3)
\r
367 self.setLayout(layout)
\r
369 self.h_wall_menu = self.create_wall_menu("h")
\r
370 self.v_wall_menu = self.create_wall_menu("v")
\r
372 self.mapframe.mouse_moved.connect(self.mouse_moved)
\r
373 self.mapframe.mouse_released.connect(self.mouse_released)
\r
374 self.mapframe.mouse_drag_released.connect(self.mouse_drag_released)
\r
375 self.create_wall_menu_triggered_signal.connect(
\r
376 self.create_wall_menu_triggered)
\r
377 self.setbackcolorbutton.clicked.connect(
\r
378 self.setbackcolorbutton_clicked)
\r
380 def create_wall_menu(self, direction):
\r
381 menu = QtGui.QMenu(self)
\r
382 for idx, img in enumerate(_mapimages.wall_icons):
\r
383 act = QtGui.QAction(self)
\r
384 act.setIcon(img[direction])
\r
388 self.create_wall_menu_triggered_signal.emit(menu.x, menu.y, direction, idx)
\r
391 act.triggered.connect(triggerd(idx))
\r
392 menu.addAction(act)
\r
395 @QtCore.Slot(int, int, int)
\r
396 def mouse_moved(self, x=0, y=0, b=QtCore.Qt.MouseButton.NoButton):
\r
397 cood = u"({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))
\r
398 self.detail.setText(cood + _mapengine.getdetail(x, y))
\r
399 self.mapframe.repaint()
\r
401 @QtCore.Slot(int, int, int, int, int)
\r
402 def mouse_drag_released(self, x1, y1, x2, y2, eraseonly):
\r
403 if self.boxdrawbutton.isChecked():
\r
404 _mapengine.growwall(x1, y1, x2, y2, eraseonly, True)
\r
405 elif self.growdrawbutton.isChecked():
\r
406 _mapengine.growwall(x1, y1, x2, y2, eraseonly, False)
\r
407 elif self.backcolorbutton.isChecked():
\r
411 backcolor = getcolorstring(self.backcolorbox.color)
\r
412 _mapengine.fillbackcolor(x1, y1, x2, y2, backcolor)
\r
413 _undomanager.save(_mapengine.savestring())
\r
414 self.mapframe.repaint()
\r
416 @QtCore.Slot(int, int, str)
\r
417 def mouse_released(self, x1, y1, direction):
\r
419 if self.mapframe.setoriginemode:
\r
420 dlg = SetOrigineDialog(self)
\r
421 dlg.setcurrent(_mapengine.viewx(x1), _mapengine.viewy(y1))
\r
422 dlg.exec_() #showでは処理がとまらない。
\r
423 if dlg.result() == QtGui.QDialog.Accepted:
\r
424 _mapengine.setoffset(
\r
425 dlg.originex - _mapengine.viewx(x1) + _mapengine.offsetx,
\r
426 dlg.originey - _mapengine.viewy(y1) + _mapengine.offsety
\r
428 _undomanager.save(_mapengine.savestring())
\r
429 self.mapframe.setoriginemode = False
\r
432 if direction == "c":
\r
433 dlg = DetailDialog(self)
\r
434 dlg.setvalue(_mapengine.viewx(x1), _mapengine.viewy(y1),
\r
435 _mapengine.getmark(x1, y1), _mapengine.getdetail(x1, y1),
\r
436 getcolorfromstring(_mapengine.getforecolor(x1, y1)))
\r
437 dlg.exec_() #showでは処理がとまらない。
\r
438 if dlg.result() == QtGui.QDialog.Accepted:
\r
439 forecolor = getcolorstring(dlg.forecolorbox.color)
\r
440 _mapengine.setmark(x1, y1, dlg.marktext.text())
\r
441 _mapengine.setdetail(x1, y1, dlg.detailtext.toPlainText())
\r
442 _mapengine.setforecolor(x1, y1, forecolor)
\r
443 _undomanager.save(_mapengine.savestring())
\r
444 self.mapframe.repaint()
\r
448 if direction == "h":
\r
449 menu = self.h_wall_menu
\r
450 elif direction == "v":
\r
451 menu = self.v_wall_menu
\r
454 menu.popup(QtGui.QCursor.pos())
\r
456 @QtCore.Slot(int, int, str, int)
\r
457 def create_wall_menu_triggered(self, x1, y1, direction, wall):
\r
458 _mapengine.setdata(x1, y1, direction, wall)
\r
459 _undomanager.save(_mapengine.savestring())
\r
460 self.mapframe.repaint()
\r
463 def setbackcolorbutton_clicked(self):
\r
465 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
466 dlg.setCurrentColor(self.backcolorbox.color)
\r
468 config["customColor"] = dlg.config
\r
469 if dlg.result() == QtGui.QDialog.Accepted:
\r
470 self.backcolorbox.color = dlg.currentColor()
\r
471 self.backcolorbutton.setChecked(True)
\r
474 class MapFrame(QtGui.QFrame):
\r
475 mouse_moved = QtCore.Signal(int, int, int)
\r
476 mouse_released = QtCore.Signal(int, int, str)
\r
477 mouse_drag_released = QtCore.Signal(int, int, int, int, int)
\r
481 def __init__(self, parent=None):
\r
482 super(MapFrame, self).__init__(parent)
\r
483 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
492 self._dragging = False
\r
493 self.setoriginemode = False
\r
494 self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
496 _mapimages.width * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
497 _mapimages.height * (_mapengine.height) + _mapimages.heightoffset * 2
\r
500 def paintEvent(self, event):
\r
501 painter = QtGui.QPainter(self)
\r
502 painter.fillRect(0, 0, self.width(), self.height(), QtGui.QColor(255, 255, 255))
\r
503 w = _mapimages.width - 1
\r
504 v = _mapimages.height - 1
\r
505 ho = _mapimages.heightoffset
\r
506 wo = _mapimages.widthoffset
\r
510 w * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
511 v * (_mapengine.height) + _mapimages.heightoffset * 2
\r
515 for x in range(_mapengine.width):
\r
517 for y in range(_mapengine.height):
\r
519 backcolor = _mapengine.getbackcolor(x, y)
\r
521 painter.fillRect(wo + xx, ho + yy, w, v,
\r
522 getcolorfromstring(backcolor))
\r
525 for x in range(_mapengine.width + 1):
\r
527 for y in range(_mapengine.height + 1):
\r
529 if x != _mapengine.width:
\r
530 painter.drawImage(wo + xx, yy,
\r
531 _mapimages.wall(0, "h"))
\r
532 if y != _mapengine.height:
\r
533 painter.drawImage(xx, ho + yy,
\r
534 _mapimages.wall(0, "v"))
\r
537 for x in range(_mapengine.width + 1):
\r
539 for y in range(_mapengine.height + 1):
\r
541 if x != _mapengine.width and _mapengine.getdata(x, y, "h") != 0:
\r
542 painter.drawImage(wo + xx, yy,
\r
543 _mapimages.wall(_mapengine.getdata(x, y, "h"), "h"))
\r
544 if y != _mapengine.height and _mapengine.getdata(x, y, "v") != 0:
\r
545 painter.drawImage(xx, ho + yy,
\r
546 _mapimages.wall(_mapengine.getdata(x, y, "v"), "v"))
\r
547 mark = _mapengine.getmark(x, y)
\r
549 painter.setPen(getcolorfromstring(_mapengine.getforecolor(x, y)))
\r
550 painter.drawText(wo + xx + 2, ho + yy + 2, w - 2, v - 2,
\r
551 QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter,
\r
554 #座標設定中はdrawing box を表示しない。
\r
555 if self.setoriginemode:
\r
559 if self._pressedbutton != QtCore.Qt.MouseButton.NoButton:
\r
560 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
561 if self._x1 == self._x2 and self._y1 == self._y2:
\r
562 painter.setPen(QtGui.QColor(255, 0, 0))
\r
563 elif self._x1 == self._x2 or self._y1 == self._y2:
\r
564 painter.setPen(QtGui.QColor(0, 255, 0))
\r
566 painter.setPen(QtGui.QColor(255, 0, 0))
\r
567 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
568 painter.setPen(QtGui.QColor(0, 0, 255))
\r
569 painter.drawRect(self._px1, self._py1,
\r
570 self._px2 - self._px1, self._py2 - self._py1)
\r
572 def eventFilter(self, obj, event):
\r
574 return ((event.pos().x() - _mapimages.widthoffset) // (_mapimages.width - 1))
\r
577 return ((event.pos().y() - _mapimages.heightoffset) // (_mapimages.height - 1))
\r
582 if et == QtCore.QEvent.MouseButtonPress:
\r
585 self._pos1 = event.pos()
\r
586 self._px1 = event.pos().x()
\r
587 self._py1 = event.pos().y()
\r
590 self._px2 = event.pos().x()
\r
591 self._py2 = event.pos().y()
\r
592 self._pressedbutton = event.buttons()
\r
593 self._dragging = False
\r
596 elif et == QtCore.QEvent.MouseMove:
\r
599 self._px2 = event.pos().x()
\r
600 self._py2 = event.pos().y()
\r
601 if (self._pressedbutton != QtCore.Qt.MouseButton.NoButton and
\r
602 (event.pos() - self._pos1).manhattanLength() >=
\r
603 QtGui.QApplication.startDragDistance()):
\r
604 self._dragging = True
\r
605 self.mouse_moved.emit(self._x2, self._y2, event.buttons())
\r
608 elif et == QtCore.QEvent.MouseButtonRelease:
\r
610 release_emit = False
\r
613 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
615 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
618 release_emit = True
\r
619 if self.setoriginemode:
\r
620 release_emit = True
\r
622 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
623 self._dragging = False
\r
625 self.mouse_drag_released.emit(
\r
626 self._x1, self._y1, self._x2, self._y2, eraseonly)
\r
628 rpx = self._px2 - self._x2 * (_mapimages.width - 1) - _mapimages.widthoffset
\r
629 rpy = self._py2 - self._y2 * (_mapimages.height - 1) - _mapimages.heightoffset
\r
630 rdx = rpx - (_mapimages.width - 1) // 2
\r
631 rdy = rpy - (_mapimages.height - 1) // 2
\r
632 if rpx <= _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
636 elif rpx >= _mapimages.width - _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
640 elif rpy <= _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
644 elif rpy >= _mapimages.height - _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
652 self.mouse_released.emit(rx, ry, d)
\r
658 # pass the event on to the parent class
\r
662 class ColorBox(QtGui.QFrame):
\r
663 def __init__(self, parent=None):
\r
664 super(ColorBox, self).__init__(parent)
\r
665 self.color = QtGui.QColor(255, 255, 255)
\r
666 self.bordercolor = QtGui.QColor(0, 0, 0)
\r
668 def paintEvent(self, event):
\r
669 painter = QtGui.QPainter(self)
\r
670 painter.fillRect(0, 0, self.width(), self.height(), self.color)
\r
671 painter.setPen(self.bordercolor)
\r
672 painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
\r
675 class DetailDialog(QtGui.QDialog):
\r
676 def __init__(self, parent=None):
\r
677 super(DetailDialog, self).__init__(parent)
\r
679 marklabel = QtGui.QLabel(self)
\r
680 marklabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
681 marklabel.setText(u"マーク(&M)")
\r
682 marklabel.setSizePolicy(
\r
683 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
685 self.marktext = QtGui.QLineEdit(self)
\r
686 self.marktext.setMaxLength(1)
\r
687 self.marktext.setText(u"")
\r
688 self.marktext.setMinimumWidth(20)
\r
689 self.marktext.setSizePolicy(
\r
690 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
691 marklabel.setBuddy(self.marktext)
\r
693 self.forecolorbutton = QtGui.QPushButton(self)
\r
694 self.forecolorbutton.setText(u"文字色(&C)...")
\r
695 self.forecolorbutton.clicked.connect(self.forecolorbutton_clicked)
\r
697 self.forecolorbox = ColorBox(self)
\r
698 self.forecolorbox.setMinimumSize(30, 30)
\r
699 self.forecolorbox.setSizePolicy(
\r
700 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
702 detaillabel = QtGui.QLabel(self)
\r
703 detaillabel.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight)
\r
704 detaillabel.setText(u"詳細(&D)")
\r
706 self.detailtext = QtGui.QTextEdit(self)
\r
707 self.detailtext.setText(u"")
\r
708 detaillabel.setBuddy(self.detailtext)
\r
710 self.buttonbox = QtGui.QDialogButtonBox(
\r
711 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
712 self.buttonbox.accepted.connect(self.accept)
\r
713 self.buttonbox.rejected.connect(self.reject)
\r
714 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
715 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
717 layout = QtGui.QGridLayout()
\r
718 layout.addWidget(marklabel, 0, 0, 1, 1)
\r
719 layout.addWidget(self.marktext, 0, 1, 1, 1)
\r
720 layout.addWidget(self.forecolorbutton, 0, 2, 1, 1)
\r
721 layout.addWidget(self.forecolorbox, 0, 3, 1, 1)
\r
722 layout.addWidget(detaillabel, 1, 0, 1, 1)
\r
723 layout.addWidget(self.detailtext, 1, 1, 1, 3)
\r
724 layout.addWidget(self.buttonbox, 2, 0, 1, 4)
\r
725 self.setLayout(layout)
\r
726 self.setModal(True)
\r
728 def setvalue(self, x, y, mark, detail, color):
\r
729 self.setWindowTitle("({x}, {y})".format(x=x, y=y))
\r
730 self.marktext.setText(mark)
\r
731 self.detailtext.setText(detail)
\r
732 self.forecolorbox.color = color
\r
734 def forecolorbutton_clicked(self):
\r
736 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
737 dlg.setCurrentColor(self.forecolorbox.color)
\r
739 config["customColor"] = dlg.config
\r
740 if dlg.result() == QtGui.QDialog.Accepted:
\r
741 self.forecolorbox.color = dlg.currentColor()
\r
744 class SetOrigineDialog(QtGui.QDialog):
\r
745 def __init__(self, parent=None):
\r
746 super(SetOrigineDialog, self).__init__(parent)
\r
747 self.setWindowTitle(u"座標設定")
\r
749 promptlabel = QtGui.QLabel(self)
\r
750 promptlabel.setAlignment(
\r
751 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
752 promptlabel.setText(u"この地点の座標を入力してください。")
\r
754 self.currentlabel = QtGui.QLabel(self)
\r
755 self.currentlabel.setAlignment(
\r
756 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
\r
757 self.currentlabel.setText(u"")
\r
759 xlabel = QtGui.QLabel(self)
\r
760 xlabel.setAlignment(
\r
761 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
762 xlabel.setText(u"&X")
\r
764 self.xbox = QtGui.QSpinBox(self)
\r
765 self.xbox.setRange(-999, +999)
\r
766 self.xbox.setSingleStep(1)
\r
767 self.xbox.setValue(0)
\r
768 xlabel.setBuddy(self.xbox)
\r
770 ylabel = QtGui.QLabel(self)
\r
771 ylabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
772 ylabel.setText(u"&Y")
\r
774 self.ybox = QtGui.QSpinBox(self)
\r
775 self.ybox.setRange(-999, +999)
\r
776 self.ybox.setSingleStep(1)
\r
777 self.ybox.setValue(0)
\r
778 ylabel.setBuddy(self.ybox)
\r
780 self.buttonbox = QtGui.QDialogButtonBox(
\r
781 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
782 self.buttonbox.accepted.connect(self.accept)
\r
783 self.buttonbox.rejected.connect(self.reject)
\r
784 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
785 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
787 layout = QtGui.QGridLayout()
\r
788 layout.addWidget(promptlabel, 0, 0, 1, 4)
\r
789 layout.addWidget(self.currentlabel, 1, 0, 1, 4)
\r
790 layout.addWidget(xlabel, 2, 0, 1, 1)
\r
791 layout.addWidget(self.xbox, 2, 1, 1, 1)
\r
792 layout.addWidget(ylabel, 2, 2, 1, 1)
\r
793 layout.addWidget(self.ybox, 2, 3, 1, 1)
\r
794 layout.addWidget(self.buttonbox, 3, 0, 1, 4)
\r
795 self.setLayout(layout)
\r
796 self.setModal(True)
\r
798 def setcurrent(self, x, y):
\r
799 self.xbox.setValue(x)
\r
800 self.ybox.setValue(y)
\r
801 self.currentlabel.setText(u"現在の座標 ({x}, {y})".format(x=x, y=y))
\r
804 def originex(self):
\r
805 return self.xbox.value()
\r
808 def originey(self):
\r
809 return self.ybox.value()
\r
812 class SetSizeDialog(QtGui.QDialog):
\r
813 def __init__(self, parent=None):
\r
814 super(SetSizeDialog, self).__init__(parent)
\r
815 self.setWindowTitle(u"マップのサイズ")
\r
817 self.topbutton = QtGui.QRadioButton(self)
\r
818 self.topbutton.setText(u"上(&T)")
\r
819 self.topbutton.clicked.connect(self.updatewidgets)
\r
821 self.topsize = QtGui.QSpinBox(self)
\r
822 self.topsize.setSingleStep(1)
\r
823 self.topsize.setValue(0)
\r
824 self.topsize.valueChanged.connect(self.updatewidgets)
\r
826 self.bottombutton = QtGui.QRadioButton(self)
\r
827 self.bottombutton.setText(u"下(&B)")
\r
828 self.bottombutton.clicked.connect(self.updatewidgets)
\r
830 self.bottomsize = QtGui.QSpinBox(self)
\r
831 self.bottomsize.setSingleStep(1)
\r
832 self.bottomsize.setValue(0)
\r
833 self.bottomsize.valueChanged.connect(self.updatewidgets)
\r
835 self.leftbutton = QtGui.QRadioButton(self)
\r
836 self.leftbutton.setText(u"左(&L)")
\r
837 self.leftbutton.clicked.connect(self.updatewidgets)
\r
839 self.leftsize = QtGui.QSpinBox(self)
\r
840 self.leftsize.setSingleStep(1)
\r
841 self.leftsize.setValue(0)
\r
842 self.leftsize.valueChanged.connect(self.updatewidgets)
\r
844 self.rightbutton = QtGui.QRadioButton(self)
\r
845 self.rightbutton.setText(u"右(&R)")
\r
846 self.rightbutton.clicked.connect(self.updatewidgets)
\r
848 self.rightsize = QtGui.QSpinBox(self)
\r
849 self.rightsize.setSingleStep(1)
\r
850 self.rightsize.setValue(0)
\r
851 self.rightsize.valueChanged.connect(self.updatewidgets)
\r
853 self.sizelabel = QtGui.QLabel(self)
\r
854 self.sizelabel .setAlignment(
\r
855 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
856 self.sizelabel.setText(u"この地点の座標を入力してください。")
\r
858 self.buttonbox = QtGui.QDialogButtonBox(
\r
859 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
860 self.buttonbox.accepted.connect(self.accept)
\r
861 self.buttonbox.rejected.connect(self.reject)
\r
862 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
863 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
865 verticalgroup = QtGui.QButtonGroup(self)
\r
866 verticalgroup.addButton(self.topbutton)
\r
867 verticalgroup.addButton(self.bottombutton)
\r
869 holizontalgroup = QtGui.QButtonGroup(self)
\r
870 holizontalgroup.addButton(self.leftbutton)
\r
871 holizontalgroup.addButton(self.rightbutton)
\r
873 self.topbutton.setChecked(True)
\r
874 self.bottombutton.setChecked(False)
\r
875 self.leftbutton.setChecked(True)
\r
876 self.rightbutton.setChecked(False)
\r
878 layout = QtGui.QGridLayout(self)
\r
879 layout.addWidget(self.topbutton, 0, 2, 1, 1)
\r
880 layout.addWidget(self.topsize, 0, 3, 1, 1)
\r
881 layout.addWidget(self.leftbutton, 1, 0, 1, 1)
\r
882 layout.addWidget(self.leftsize, 1, 1, 1, 1)
\r
883 layout.addWidget(self.sizelabel, 1, 2, 1, 2)
\r
884 layout.addWidget(self.rightbutton, 1, 4, 1, 1)
\r
885 layout.addWidget(self.rightsize, 1, 5, 1, 1)
\r
886 layout.addWidget(self.bottombutton, 2, 2, 1, 1)
\r
887 layout.addWidget(self.bottomsize, 2, 3, 1, 1)
\r
888 layout.addWidget(self.buttonbox, 3, 0, 1, 6)
\r
889 self.setLayout(layout)
\r
890 self.setModal(True)
\r
892 def setoriginalsize(self, width, height):
\r
893 self._width = width
\r
894 self._height = height
\r
895 self.topsize.setRange(-height+1, +100)
\r
896 self.bottomsize.setRange(-height+1, +100)
\r
897 self.leftsize.setRange(-width+1, +100)
\r
898 self.rightsize.setRange(-width+1, +100)
\r
899 self.updatewidgets()
\r
901 def updatewidgets(self):
\r
905 if self.topbutton.isChecked():
\r
906 dh = self.topsize.value()
\r
907 self.topsize.setEnabled(True)
\r
908 self.bottomsize.setDisabled(True)
\r
909 elif self.bottombutton.isChecked():
\r
910 dh = self.bottomsize.value()
\r
911 self.topsize.setDisabled(True)
\r
912 self.bottomsize.setEnabled(True)
\r
913 if self.leftbutton.isChecked():
\r
914 dw = self.leftsize.value()
\r
915 self.leftsize.setEnabled(True)
\r
916 self.rightsize.setDisabled(True)
\r
917 elif self.rightbutton.isChecked():
\r
918 dw = self.rightsize.value()
\r
919 self.leftsize.setDisabled(True)
\r
920 self.rightsize.setEnabled(True)
\r
922 self.sizelabel.setText(
\r
923 u"変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(
\r
924 w1=self._width, h1=self._height,
\r
925 w2=self._width+dw, h2=self._height+dh))
\r
932 if self.topbutton.isChecked():
\r
933 top = self.topsize.value()
\r
934 elif self.bottombutton.isChecked():
\r
935 bottom = self.bottomsize.value()
\r
936 if self.leftbutton.isChecked():
\r
937 left = self.leftsize.value()
\r
938 elif self.rightbutton.isChecked():
\r
939 right = self.rightsize.value()
\r
940 return (top, bottom, left, right)
\r
943 class MapImages(object):
\r
944 def __init__(self):
\r
945 self.wall_images = list()
\r
946 self.wall_icons = list()
\r
947 for index in range(10):
\r
948 self.wall_images.append(dict())
\r
949 self.wall_icons.append(dict())
\r
950 for direction in ["v", "h"]:
\r
951 filename = os.path.join(
\r
954 u"wall_{direction}_{index:02}.png".format(
\r
955 direction=direction, index=index))
\r
956 self.wall_images[index][direction] = QtGui.QImage()
\r
957 self.wall_images[index][direction].load(filename)
\r
958 self.wall_icons[index][direction] = QtGui.QIcon(filename)
\r
962 return self.wall_images[0]["h"].width()
\r
966 return self.wall_images[0]["v"].height()
\r
969 def widthoffset(self):
\r
970 return self.wall_images[0]["v"].width()//2
\r
973 def heightoffset(self):
\r
974 return self.wall_images[0]["h"].height()//2
\r
976 def wall(self, index, direction):
\r
977 return self.wall_images[index][direction]
\r
980 class MapEngine(object):
\r
981 hwall = " -#WMwmHVA"
\r
982 vwall = " |#PCpc=DG"
\r
984 def __init__(self, width, height, signx, signy, offsetx, offsety):
\r
985 self._width = width
\r
986 self._height = height
\r
987 self._signx = signx
\r
988 self._signy = signy
\r
989 self._offsetx = offsetx
\r
990 self._offsety = offsety
\r
991 self.filename = None
\r
995 self._note = dict()
\r
997 def initdata(self):
\r
998 width = self.width + 1
\r
999 height = self.height + 1
\r
1000 self._data = self.initialdata(width, height)
\r
1002 def initialdata(self, width, height):
\r
1004 for x in range(width):
\r
1006 for y in range(height):
\r
1007 dt[x].append(dict())
\r
1008 for d in ["h", "v"]:
\r
1012 def inityaml(self):
\r
1013 #yaml !python/Unicode出力抑止おまじない
\r
1014 def represent_unicode(dumper, data):
\r
1015 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
\r
1016 def construct_unicode(loader, node):
\r
1017 return unicode(loader.construct_scalar(node))
\r
1018 yaml.add_representer(unicode, represent_unicode)
\r
1019 yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)
\r
1021 def getdata(self, x, y, direction):
\r
1022 return self._data[x][y][direction]
\r
1024 def setdata(self, x, y, direction, value):
\r
1025 self._data[x][y][direction] = value
\r
1029 return self._width
\r
1033 return self._height
\r
1037 return self._signx
\r
1041 return self._signy
\r
1044 def offsetx(self):
\r
1045 return self._offsetx
\r
1048 def offsety(self):
\r
1049 return self._offsety
\r
1051 def setoffset(self, x, y):
\r
1055 def getmark(self, x, y):
\r
1056 return self.unescape(self.getnote(x, y)["mark"])
\r
1058 def getdetail(self, x, y):
\r
1059 return self.unescape(self.getnote(x, y)["detail"])
\r
1061 def getforecolor(self, x, y):
\r
1062 return self.getnote(x, y)["forecolor"]
\r
1064 def getbackcolor(self, x, y):
\r
1065 return self.getnote(x, y)["backcolor"]
\r
1067 def getnote(self, x, y):
\r
1068 return self._note.get(
\r
1069 self.coodtokey(x, y), {"mark":u"", "detail":u"", "forecolor":u"#000000", "backcolor":u""})
\r
1071 def coodtokey(self, x, y):
\r
1072 return u"{x:+05d}_{y:+05d}".format(x=x, y=y)
\r
1074 def keytocood(self, key):
\r
1075 return map(int, key.split("_"))
\r
1077 def setmark(self, x, y, mark):
\r
1078 note = self.getnote(x, y)
\r
1079 note["mark"] = self.escape(mark)
\r
1080 self.setnote(x, y, note)
\r
1082 def setdetail(self, x, y, detail):
\r
1083 note = self.getnote(x, y)
\r
1084 note["detail"] = self.escape(detail)
\r
1085 self.setnote(x, y, note)
\r
1087 def setforecolor(self, x, y, color):
\r
1088 note = self.getnote(x, y)
\r
1089 note["forecolor"] = color
\r
1090 self.setnote(x, y, note)
\r
1092 def setbackcolor(self, x, y, color):
\r
1093 note = self.getnote(x, y)
\r
1094 note["backcolor"] = color
\r
1095 self.setnote(x, y, note)
\r
1097 def setnote(self, x, y, note):
\r
1098 self._note[self.coodtokey(x, y)] = note
\r
1100 def escape(self, s):
\r
1101 return s.replace("\\", "\\\\").replace("\n", r"\n")
\r
1103 def unescape(self, s):
\r
1104 return s.replace(r"\n", "\n").replace("\\\\", "\\")
\r
1106 def viewx(self, x):
\r
1107 return x * self.signx + self.offsetx
\r
1109 def viewy(self, y):
\r
1110 return y * self.signy + self.offsety
\r
1112 def worldx(self, x):
\r
1113 return (x - self.offsetx) / self.signx
\r
1115 def worldy(self, y):
\r
1116 return (y - self.offsety) / self.signy
\r
1118 def changesize(self, top, bottom, left, right):
\r
1119 oldoffsetx = max(-left, 0)
\r
1120 newoffsetx = max(left, 0)
\r
1121 newwidth = self.width + left + right
\r
1122 oldoffsety = max(-top, 0)
\r
1123 newoffsety = max(top, 0)
\r
1124 newheight = self.height + top + bottom
\r
1126 newdata = self.initialdata(newwidth + 1, newheight + 1)
\r
1128 for x in range(min(self._width, newwidth) + 1):
\r
1129 for y in range(min(self._height, newheight) + 1):
\r
1130 for d in ["h", "v"]:
\r
1131 newdata[x+newoffsetx][y+newoffsety][d] = self._data[x+oldoffsetx][y+oldoffsety][d]
\r
1132 newnote[self.coodtokey(x+newoffsetx, y+newoffsety)] = self.getnote(x+oldoffsetx, y+oldoffsety)
\r
1133 self._width = newwidth
\r
1134 self._height = newheight
\r
1135 self.setoffset(self.offsetx -self.signx * left, self.offsety -self.signy * top)
\r
1136 self._data = newdata
\r
1137 self._note = newnote
\r
1139 def growwall(self, x1, y1, x2, y2, eraseonly, alwaysbox):
\r
1140 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1141 offsetx, offsety = self.getoffset(x1, y1, x2, y2)
\r
1143 #delete inner walls.
\r
1144 for x in range(x1, x2+stepx, stepx):
\r
1145 for y in range(y1+stepy+offsety, y2+stepy+offsety, stepy):
\r
1146 self._data[x][y]["h"] = 0
\r
1147 for x in range(x1+stepx+offsetx, x2+stepx+offsetx, stepx):
\r
1148 for y in range(y1, y2+stepy, stepy):
\r
1149 self._data[x][y]["v"] = 0
\r
1152 #draw OUTER wall if it exists.
\r
1153 if alwaysbox or (x1 == x2 and y1 == y2):
\r
1166 for x in range(x1, x2+stepx, stepx):
\r
1167 if not (vline and x == x1):
\r
1169 if self._data[x][y1+offsety]["h"] == 0:
\r
1170 self._data[x][y1+offsety]["h"] = 1
\r
1171 if self._data[x][y2+stepy+offsety]["h"] == 0:
\r
1172 self._data[x][y2+stepy+offsety]["h"] = 1
\r
1173 for y in range(y1, y2+stepy, stepy):
\r
1174 if not (hline and y == y1):
\r
1176 if self._data[x1+offsetx][y]["v"] == 0:
\r
1177 self._data[x1+offsetx][y]["v"] = 1
\r
1178 if self._data[x2+stepx+offsetx][y]["v"] == 0:
\r
1179 self._data[x2+stepx+offsetx][y]["v"] = 1
\r
1181 def fillbackcolor(self, x1, y1, x2, y2, backcolor):
\r
1182 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1183 for x in range(x1, x2+stepx, stepx):
\r
1184 for y in range(y1, y2+stepy, stepy):
\r
1185 self.setbackcolor(x, y, backcolor)
\r
1187 def getstep(self, x1, y1, x2, y2):
\r
1196 return (stepx, stepy)
\r
1198 def getoffset(self, x1, y1, x2, y2):
\r
1207 return (offsetx, offsety)
\r
1209 def save(self, filename):
\r
1210 dt = self.savestring()
\r
1211 with codecs.open(filename, "w") as f:
\r
1213 self.filename = filename
\r
1215 def savestring(self):
\r
1217 data["size"] = {"x":self.width, "y":self.height}
\r
1218 data["offset"] = {"x":self.offsetx, "y":self.offsety}
\r
1219 data["sign"] = {"x":self.signx, "y":self.signy}
\r
1220 data["map"] = self.getmapstring()
\r
1224 for nk, ni in self._note.items():
\r
1225 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"]:
\r
1226 x, y = self.keytocood(nk)
\r
1227 n[self.coodtokey(self.viewx(x), self.viewy(y))] = ni
\r
1229 return yaml.safe_dump(data, allow_unicode=True,
\r
1230 default_flow_style=False, encoding='utf-8')
\r
1232 def getmapstring(self):
\r
1235 for y in range(self.height):
\r
1237 for x in range(self.width):
\r
1239 s.append(self.hwall[self._data[x][y]["h"]])
\r
1242 m.append("".join(s))
\r
1244 for x in range(self.width):
\r
1245 s.append(self.vwall[self._data[x][y]["v"]])
\r
1247 s.append(self.vwall[self._data[self.width][y]["v"]])
\r
1249 m.append("".join(s))
\r
1252 for x in range(self.width):
\r
1254 s.append(self.hwall[self._data[x][y]["h"]])
\r
1257 m.append("".join(s))
\r
1260 def load(self, filename):
\r
1261 with codecs.open(filename, "r", encoding="utf-8") as f:
\r
1263 self.loadfromstring(st)
\r
1264 self.filename = filename
\r
1266 def loadfromstring(self, st):
\r
1267 data = yaml.safe_load(st)
\r
1270 self._width = data["size"]["x"]
\r
1271 self._height = data["size"]["y"]
\r
1272 self._signx = data["sign"]["x"]
\r
1273 self._signy = data["sign"]["y"]
\r
1274 self._offsetx = data["offset"]["x"]
\r
1275 self._offsety = data["offset"]["y"]
\r
1279 for y in range(self.height):
\r
1280 for x in range(self.width):
\r
1281 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1282 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1284 for y in range(self.height):
\r
1285 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1287 for x in range(self.width):
\r
1288 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1292 for nk, ni in data["note"].items():
\r
1293 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":
\r
1294 x, y = self.keytocood(nk)
\r
1295 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni
\r
1300 class UndoManager(QtCore.QObject):
\r
1301 MAX_UNDO_COUNT = 128
\r
1302 changed = QtCore.Signal(bool, bool)
\r
1304 def __init__(self):
\r
1305 super(UndoManager, self).__init__()
\r
1309 self._undo = [None for x in range(self.MAX_UNDO_COUNT)]
\r
1311 self._undocount = 0
\r
1312 self._commited = True
\r
1313 self.changed.emit(self.canundo, self.canredo)
\r
1315 def init(self, data):
\r
1318 self._commited = True
\r
1320 def save(self, obj):
\r
1321 if self._index >= self.MAX_UNDO_COUNT:
\r
1322 self._undo = self._undo[1:]
\r
1324 self._undo.append(None)
\r
1325 self._undo[self._index] = obj
\r
1327 self._undocount = 0
\r
1328 self._commited = False
\r
1329 self.changed.emit(self.canundo, self.canredo)
\r
1333 self._undocount += 1
\r
1334 self._commited = False
\r
1335 self.changed.emit(self.canundo, self.canredo)
\r
1336 return self._undo[self._index - 1]
\r
1340 self._undocount -= 1
\r
1341 self._commited = False
\r
1342 self.changed.emit(self.canundo, self.canredo)
\r
1343 return self._undo[self._index - 1]
\r
1346 self._commited = True
\r
1349 def canundo(self):
\r
1350 return (self._index > 1)
\r
1353 def canredo(self):
\r
1354 return (self._undocount > 0)
\r
1357 def commited(self):
\r
1358 return self._commited
\r
1361 class PydunColorDialog(QtGui.QColorDialog):
\r
1362 def __init__(self, parent, config):
\r
1363 super(PydunColorDialog, self).__init__(parent)
\r
1364 for index in range(self.customCount()):
\r
1365 self.setCustomColor(index,
\r
1366 getcolorfromstring(
\r
1367 config.get(index, "#FFFFFF")).rgb())
\r
1368 self.updateconfig()
\r
1370 def updateconfig(self):
\r
1371 self._config = dict()
\r
1372 for index in range(self.customCount()):
\r
1373 self._config[index] = getcolorstring(
\r
1374 QtGui.QColor.fromRgb(self.customColor(index)))
\r
1377 super(PydunColorDialog, self).exec_()
\r
1378 self.updateconfig()
\r
1382 return self._config
\r
1385 class PydunAskSaveDialog(QtGui.QMessageBox):
\r
1386 def __init__(self, parent, filename):
\r
1387 super(PydunAskSaveDialog, self).__init__(parent)
\r
1388 self.setText(u"{filename} への変更を保存しますか?".format(filename=filename))
\r
1389 self.setStandardButtons(QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel)
\r
1390 self.setDefaultButton(QtGui.QMessageBox.Save)
\r
1391 self.button(QtGui.QMessageBox.Save).setText(u"保存する(&S)")
\r
1392 self.button(QtGui.QMessageBox.Discard).setText(u"保存しない(&N)")
\r
1393 self.button(QtGui.QMessageBox.Cancel).setText(u"キャンセル")
\r
1396 def getcolorstring(color):
\r
1397 return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())
\r
1399 def getcolorfromstring(colorstring):
\r
1400 return QtGui.QColor.fromRgb(
\r
1401 int(colorstring[1:3], 16),
\r
1402 int(colorstring[3:5], 16),
\r
1403 int(colorstring[5:7], 16))
\r
1406 return os.path.dirname(os.path.abspath(unicode(sys.argv[0], locale.getpreferredencoding())))
\r
1408 def getlatestversion():
\r
1410 rss = urllib.urlopen(projectrssurl)
\r
1411 rssstring = rss.read()
\r
1412 rsstree = xml.etree.ElementTree.fromstring(rssstring)
\r
1413 item = rsstree.find("channel/item/title")
\r
1414 ver = (item.text.split(" "))[2]
\r
1417 ver = projectversion
\r
1423 app = QtGui.QApplication(sys.argv)
\r
1424 mainWin = MainWindow()
\r
1425 app.installEventFilter(mainWin.centralWidget().mapframe)
\r
1427 sys.exit(app.exec_())
\r
1431 global configfilename
\r
1432 configfilename = os.path.join(
\r
1436 with open(configfilename, "r") as f:
\r
1437 config = yaml.safe_load(f)
\r
1441 if __name__ == '__main__':
\r