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(_mapimages.wall_texts[idx][direction], 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 vtext = [u"なし", u"壁", u"扉", u"扉(→)", u"扉(←)", u"一通(→)", u"一通(←)", u"隠", u"隠(→)", u"隠(←)",]
\r
946 htext = [u"なし", u"壁", u"扉", u"扉(↓)", u"扉(↑)", u"一通(↓)", u"一通(↑)", u"隠", u"隠(↓)", u"隠(↑)",]
\r
947 self.wall_images = list()
\r
948 self.wall_icons = list()
\r
949 self.wall_texts = list()
\r
950 for index in range(10):
\r
951 self.wall_images.append(dict())
\r
952 self.wall_icons.append(dict())
\r
953 self.wall_texts.append(dict())
\r
954 for direction in ["v", "h"]:
\r
955 filename = os.path.join(
\r
958 u"wall_{direction}_{index:02}.png".format(
\r
959 direction=direction, index=index))
\r
960 self.wall_images[index][direction] = QtGui.QImage()
\r
961 self.wall_images[index][direction].load(filename)
\r
962 self.wall_icons[index][direction] = QtGui.QIcon(filename)
\r
963 self.wall_texts[index]["v"] = vtext[index]
\r
964 self.wall_texts[index]["h"] = htext[index]
\r
968 return self.wall_images[0]["h"].width()
\r
972 return self.wall_images[0]["v"].height()
\r
975 def widthoffset(self):
\r
976 return self.wall_images[0]["v"].width()//2
\r
979 def heightoffset(self):
\r
980 return self.wall_images[0]["h"].height()//2
\r
982 def wall(self, index, direction):
\r
983 return self.wall_images[index][direction]
\r
986 class MapEngine(object):
\r
987 hwall = " -#WMwmHVA"
\r
988 vwall = " |#PCpc=DG"
\r
990 def __init__(self, width, height, signx, signy, offsetx, offsety):
\r
991 self._width = width
\r
992 self._height = height
\r
993 self._signx = signx
\r
994 self._signy = signy
\r
995 self._offsetx = offsetx
\r
996 self._offsety = offsety
\r
997 self.filename = None
\r
1001 self._note = dict()
\r
1003 def initdata(self):
\r
1004 width = self.width + 1
\r
1005 height = self.height + 1
\r
1006 self._data = self.initialdata(width, height)
\r
1008 def initialdata(self, width, height):
\r
1010 for x in range(width):
\r
1012 for y in range(height):
\r
1013 dt[x].append(dict())
\r
1014 for d in ["h", "v"]:
\r
1018 def inityaml(self):
\r
1019 #yaml !python/Unicode出力抑止おまじない
\r
1020 def represent_unicode(dumper, data):
\r
1021 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
\r
1022 def construct_unicode(loader, node):
\r
1023 return unicode(loader.construct_scalar(node))
\r
1024 yaml.add_representer(unicode, represent_unicode)
\r
1025 yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)
\r
1027 def getdata(self, x, y, direction):
\r
1028 return self._data[x][y][direction]
\r
1030 def setdata(self, x, y, direction, value):
\r
1031 self._data[x][y][direction] = value
\r
1035 return self._width
\r
1039 return self._height
\r
1043 return self._signx
\r
1047 return self._signy
\r
1050 def offsetx(self):
\r
1051 return self._offsetx
\r
1054 def offsety(self):
\r
1055 return self._offsety
\r
1057 def setoffset(self, x, y):
\r
1061 def getmark(self, x, y):
\r
1062 return self.unescape(self.getnote(x, y)["mark"])
\r
1064 def getdetail(self, x, y):
\r
1065 return self.unescape(self.getnote(x, y)["detail"])
\r
1067 def getforecolor(self, x, y):
\r
1068 return self.getnote(x, y)["forecolor"]
\r
1070 def getbackcolor(self, x, y):
\r
1071 return self.getnote(x, y)["backcolor"]
\r
1073 def getnote(self, x, y):
\r
1074 return self._note.get(
\r
1075 self.coodtokey(x, y), {"mark":u"", "detail":u"", "forecolor":u"#000000", "backcolor":u""})
\r
1077 def coodtokey(self, x, y):
\r
1078 return u"{x:+05d}_{y:+05d}".format(x=x, y=y)
\r
1080 def keytocood(self, key):
\r
1081 return map(int, key.split("_"))
\r
1083 def setmark(self, x, y, mark):
\r
1084 note = self.getnote(x, y)
\r
1085 note["mark"] = self.escape(mark)
\r
1086 self.setnote(x, y, note)
\r
1088 def setdetail(self, x, y, detail):
\r
1089 note = self.getnote(x, y)
\r
1090 note["detail"] = self.escape(detail)
\r
1091 self.setnote(x, y, note)
\r
1093 def setforecolor(self, x, y, color):
\r
1094 note = self.getnote(x, y)
\r
1095 note["forecolor"] = color
\r
1096 self.setnote(x, y, note)
\r
1098 def setbackcolor(self, x, y, color):
\r
1099 note = self.getnote(x, y)
\r
1100 note["backcolor"] = color
\r
1101 self.setnote(x, y, note)
\r
1103 def setnote(self, x, y, note):
\r
1104 self._note[self.coodtokey(x, y)] = note
\r
1106 def escape(self, s):
\r
1107 return s.replace("\\", "\\\\").replace("\n", r"\n")
\r
1109 def unescape(self, s):
\r
1110 return s.replace(r"\n", "\n").replace("\\\\", "\\")
\r
1112 def viewx(self, x):
\r
1113 return x * self.signx + self.offsetx
\r
1115 def viewy(self, y):
\r
1116 return y * self.signy + self.offsety
\r
1118 def worldx(self, x):
\r
1119 return (x - self.offsetx) / self.signx
\r
1121 def worldy(self, y):
\r
1122 return (y - self.offsety) / self.signy
\r
1124 def changesize(self, top, bottom, left, right):
\r
1125 oldoffsetx = max(-left, 0)
\r
1126 newoffsetx = max(left, 0)
\r
1127 newwidth = self.width + left + right
\r
1128 oldoffsety = max(-top, 0)
\r
1129 newoffsety = max(top, 0)
\r
1130 newheight = self.height + top + bottom
\r
1132 newdata = self.initialdata(newwidth + 1, newheight + 1)
\r
1134 for x in range(min(self._width, newwidth) + 1):
\r
1135 for y in range(min(self._height, newheight) + 1):
\r
1136 for d in ["h", "v"]:
\r
1137 newdata[x+newoffsetx][y+newoffsety][d] = self._data[x+oldoffsetx][y+oldoffsety][d]
\r
1138 newnote[self.coodtokey(x+newoffsetx, y+newoffsety)] = self.getnote(x+oldoffsetx, y+oldoffsety)
\r
1139 self._width = newwidth
\r
1140 self._height = newheight
\r
1141 self.setoffset(self.offsetx -self.signx * left, self.offsety -self.signy * top)
\r
1142 self._data = newdata
\r
1143 self._note = newnote
\r
1145 def growwall(self, x1, y1, x2, y2, eraseonly, alwaysbox):
\r
1146 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1147 offsetx, offsety = self.getoffset(x1, y1, x2, y2)
\r
1149 #delete inner walls.
\r
1150 for x in range(x1, x2+stepx, stepx):
\r
1151 for y in range(y1+stepy+offsety, y2+stepy+offsety, stepy):
\r
1152 self._data[x][y]["h"] = 0
\r
1153 for x in range(x1+stepx+offsetx, x2+stepx+offsetx, stepx):
\r
1154 for y in range(y1, y2+stepy, stepy):
\r
1155 self._data[x][y]["v"] = 0
\r
1158 #draw OUTER wall if it exists.
\r
1159 if alwaysbox or (x1 == x2 and y1 == y2):
\r
1172 for x in range(x1, x2+stepx, stepx):
\r
1173 if not (vline and x == x1):
\r
1175 if self._data[x][y1+offsety]["h"] == 0:
\r
1176 self._data[x][y1+offsety]["h"] = 1
\r
1177 if self._data[x][y2+stepy+offsety]["h"] == 0:
\r
1178 self._data[x][y2+stepy+offsety]["h"] = 1
\r
1179 for y in range(y1, y2+stepy, stepy):
\r
1180 if not (hline and y == y1):
\r
1182 if self._data[x1+offsetx][y]["v"] == 0:
\r
1183 self._data[x1+offsetx][y]["v"] = 1
\r
1184 if self._data[x2+stepx+offsetx][y]["v"] == 0:
\r
1185 self._data[x2+stepx+offsetx][y]["v"] = 1
\r
1187 def fillbackcolor(self, x1, y1, x2, y2, backcolor):
\r
1188 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1189 for x in range(x1, x2+stepx, stepx):
\r
1190 for y in range(y1, y2+stepy, stepy):
\r
1191 self.setbackcolor(x, y, backcolor)
\r
1193 def getstep(self, x1, y1, x2, y2):
\r
1202 return (stepx, stepy)
\r
1204 def getoffset(self, x1, y1, x2, y2):
\r
1213 return (offsetx, offsety)
\r
1215 def save(self, filename):
\r
1216 dt = self.savestring()
\r
1217 with codecs.open(filename, "w") as f:
\r
1219 self.filename = filename
\r
1221 def savestring(self):
\r
1223 data["size"] = {"x":self.width, "y":self.height}
\r
1224 data["offset"] = {"x":self.offsetx, "y":self.offsety}
\r
1225 data["sign"] = {"x":self.signx, "y":self.signy}
\r
1226 data["map"] = self.getmapstring()
\r
1230 for nk, ni in self._note.items():
\r
1231 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"]:
\r
1232 x, y = self.keytocood(nk)
\r
1233 n[self.coodtokey(self.viewx(x), self.viewy(y))] = ni
\r
1235 return yaml.safe_dump(data, allow_unicode=True,
\r
1236 default_flow_style=False, encoding='utf-8')
\r
1238 def getmapstring(self):
\r
1241 for y in range(self.height):
\r
1243 for x in range(self.width):
\r
1245 s.append(self.hwall[self._data[x][y]["h"]])
\r
1248 m.append("".join(s))
\r
1250 for x in range(self.width):
\r
1251 s.append(self.vwall[self._data[x][y]["v"]])
\r
1253 s.append(self.vwall[self._data[self.width][y]["v"]])
\r
1255 m.append("".join(s))
\r
1258 for x in range(self.width):
\r
1260 s.append(self.hwall[self._data[x][y]["h"]])
\r
1263 m.append("".join(s))
\r
1266 def load(self, filename):
\r
1267 with codecs.open(filename, "r", encoding="utf-8") as f:
\r
1269 self.loadfromstring(st)
\r
1270 self.filename = filename
\r
1272 def loadfromstring(self, st):
\r
1273 data = yaml.safe_load(st)
\r
1276 self._width = data["size"]["x"]
\r
1277 self._height = data["size"]["y"]
\r
1278 self._signx = data["sign"]["x"]
\r
1279 self._signy = data["sign"]["y"]
\r
1280 self._offsetx = data["offset"]["x"]
\r
1281 self._offsety = data["offset"]["y"]
\r
1285 for y in range(self.height):
\r
1286 for x in range(self.width):
\r
1287 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1288 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1290 for y in range(self.height):
\r
1291 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1293 for x in range(self.width):
\r
1294 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1298 for nk, ni in data["note"].items():
\r
1299 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":
\r
1300 x, y = self.keytocood(nk)
\r
1301 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni
\r
1306 class UndoManager(QtCore.QObject):
\r
1307 MAX_UNDO_COUNT = 128
\r
1308 changed = QtCore.Signal(bool, bool)
\r
1310 def __init__(self):
\r
1311 super(UndoManager, self).__init__()
\r
1315 self._undo = [None for x in range(self.MAX_UNDO_COUNT)]
\r
1317 self._undocount = 0
\r
1318 self._commited = True
\r
1319 self.changed.emit(self.canundo, self.canredo)
\r
1321 def init(self, data):
\r
1324 self._commited = True
\r
1326 def save(self, obj):
\r
1327 if self._index >= self.MAX_UNDO_COUNT:
\r
1328 self._undo = self._undo[1:]
\r
1330 self._undo.append(None)
\r
1331 self._undo[self._index] = obj
\r
1333 self._undocount = 0
\r
1334 self._commited = False
\r
1335 self.changed.emit(self.canundo, self.canredo)
\r
1339 self._undocount += 1
\r
1340 self._commited = False
\r
1341 self.changed.emit(self.canundo, self.canredo)
\r
1342 return self._undo[self._index - 1]
\r
1346 self._undocount -= 1
\r
1347 self._commited = False
\r
1348 self.changed.emit(self.canundo, self.canredo)
\r
1349 return self._undo[self._index - 1]
\r
1352 self._commited = True
\r
1355 def canundo(self):
\r
1356 return (self._index > 1)
\r
1359 def canredo(self):
\r
1360 return (self._undocount > 0)
\r
1363 def commited(self):
\r
1364 return self._commited
\r
1367 class PydunColorDialog(QtGui.QColorDialog):
\r
1368 def __init__(self, parent, config):
\r
1369 super(PydunColorDialog, self).__init__(parent)
\r
1370 for index in range(self.customCount()):
\r
1371 self.setCustomColor(index,
\r
1372 getcolorfromstring(
\r
1373 config.get(index, "#FFFFFF")).rgb())
\r
1374 self.updateconfig()
\r
1376 def updateconfig(self):
\r
1377 self._config = dict()
\r
1378 for index in range(self.customCount()):
\r
1379 self._config[index] = getcolorstring(
\r
1380 QtGui.QColor.fromRgb(self.customColor(index)))
\r
1383 super(PydunColorDialog, self).exec_()
\r
1384 self.updateconfig()
\r
1388 return self._config
\r
1391 class PydunAskSaveDialog(QtGui.QMessageBox):
\r
1392 def __init__(self, parent, filename):
\r
1393 super(PydunAskSaveDialog, self).__init__(parent)
\r
1394 self.setText(u"{filename} への変更を保存しますか?".format(filename=filename))
\r
1395 self.setStandardButtons(QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel)
\r
1396 self.setDefaultButton(QtGui.QMessageBox.Save)
\r
1397 self.button(QtGui.QMessageBox.Save).setText(u"保存する(&S)")
\r
1398 self.button(QtGui.QMessageBox.Discard).setText(u"保存しない(&N)")
\r
1399 self.button(QtGui.QMessageBox.Cancel).setText(u"キャンセル")
\r
1402 def getcolorstring(color):
\r
1403 return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())
\r
1405 def getcolorfromstring(colorstring):
\r
1406 return QtGui.QColor.fromRgb(
\r
1407 int(colorstring[1:3], 16),
\r
1408 int(colorstring[3:5], 16),
\r
1409 int(colorstring[5:7], 16))
\r
1412 return os.path.dirname(os.path.abspath(unicode(sys.argv[0], locale.getpreferredencoding())))
\r
1414 def getlatestversion():
\r
1416 rss = urllib.urlopen(projectrssurl)
\r
1417 rssstring = rss.read()
\r
1418 rsstree = xml.etree.ElementTree.fromstring(rssstring)
\r
1419 item = rsstree.find("channel/item/title")
\r
1420 ver = (item.text.split(" "))[2]
\r
1423 ver = projectversion
\r
1429 app = QtGui.QApplication(sys.argv)
\r
1430 mainWin = MainWindow()
\r
1431 app.installEventFilter(mainWin.centralWidget().mapframe)
\r
1433 sys.exit(app.exec_())
\r
1437 global configfilename
\r
1438 configfilename = os.path.join(
\r
1442 with open(configfilename, "r") as f:
\r
1443 config = yaml.safe_load(f)
\r
1447 if __name__ == '__main__':
\r