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://osdn.jp/projects/pydun/
\r
7 #email: hrwatahiki at gmail.com
\r
8 #twitter: @hrwatahiki
\r
9 #blog: http://hrwatahiki.blogspot.jp/
\r
17 import xml.etree.ElementTree
\r
19 from PySide import QtCore, QtGui
\r
27 projecturl = "http://osdn.jp/projects/pydun/"
\r
28 projectrssurl = "http://osdn.jp/projects/pydun/releases/rss"
\r
29 projectversion = "1.0.6.1"
\r
32 class MainWindow(QtGui.QMainWindow):
\r
34 def __init__(self, parent=None):
\r
39 super(MainWindow, self).__init__(parent)
\r
41 _undomanager = UndoManager()
\r
42 _mapimages = MapImages(config.get("showWallMenuString", False))
\r
44 _undomanager.changed.connect(self.updateundostate)
\r
47 if len(sys.argv) >= 2:
\r
48 self.open(unicode(sys.argv[1], locale.getpreferredencoding()))
\r
50 self.mainframe = MainFrame(self)
\r
51 self.setCentralWidget(self.mainframe)
\r
53 self.statusbar = QtGui.QStatusBar(self)
\r
54 self.statusbar.showMessage(u"")
\r
55 self.setStatusBar(self.statusbar)
\r
56 if "windowSize" in config:
\r
59 config["windowSize"]["width"],
\r
60 config["windowSize"]["height"]))
\r
65 filemenu = self.menuBar().addMenu(u"ファイル(&F)")
\r
67 newact = QtGui.QAction(u"新規(&N)", self)
\r
68 newact.triggered.connect(self.new_triggered)
\r
69 newact.setShortcut(QtGui.QKeySequence.New)
\r
70 filemenu.addAction(newact)
\r
72 openact = QtGui.QAction(u"開く(&O)...", self)
\r
73 openact.triggered.connect(self.open_triggered)
\r
74 openact.setShortcut(QtGui.QKeySequence.Open)
\r
75 filemenu.addAction(openact)
\r
77 saveact = QtGui.QAction(u"上書き保存(&S)", self)
\r
78 saveact.triggered.connect(self.save_triggered)
\r
79 saveact.setShortcut(QtGui.QKeySequence.Save)
\r
80 filemenu.addAction(saveact)
\r
82 saveasact = QtGui.QAction(u"名前をつけて保存(&A)...", self)
\r
83 saveasact.triggered.connect(self.saveas_triggered)
\r
84 saveasact.setShortcut(QtGui.QKeySequence.SaveAs)
\r
85 filemenu.addAction(saveasact)
\r
87 exitact = QtGui.QAction(u"終了(&E)", self)
\r
88 exitact.triggered.connect(self.exit_triggered)
\r
89 exitact.setShortcut(QtGui.QKeySequence.Quit)
\r
90 filemenu.addAction(exitact)
\r
93 editmenu = self.menuBar().addMenu(u"編集(&E)")
\r
94 self.undoact = QtGui.QAction(u"元に戻す(&U)", self)
\r
95 self.undoact.triggered.connect(self.undo_triggered)
\r
96 self.undoact.setShortcut(QtGui.QKeySequence.Undo)
\r
97 editmenu.addAction(self.undoact)
\r
98 self.redoact = QtGui.QAction(u"やり直し(&R)", self)
\r
99 self.redoact.triggered.connect(self.redo_triggered)
\r
100 self.redoact.setShortcut(QtGui.QKeySequence.Redo)
\r
101 editmenu.addAction(self.redoact)
\r
102 editmenu.addSeparator()
\r
103 setmapsizeact = QtGui.QAction(u"マップのサイズ(&S)", self)
\r
104 setmapsizeact.triggered.connect(self.setmapsize_triggered)
\r
105 editmenu.addAction(setmapsizeact)
\r
106 setorigineact = QtGui.QAction(u"座標設定(&O)", self)
\r
107 setorigineact.triggered.connect(self.setorigine_triggered)
\r
108 editmenu.addAction(setorigineact)
\r
109 wallmenustringact = QtGui.QAction(u"壁メニューに文字を表示する(&W)", self)
\r
110 wallmenustringact.setCheckable(True)
\r
111 wallmenustringact.setChecked(config.get("showWallMenuString", False))
\r
112 wallmenustringact.triggered.connect(self.togglewallmenustring_triggered)
\r
113 editmenu.addAction(wallmenustringact)
\r
116 helpmenu = self.menuBar().addMenu(u"ヘルプ(&H)")
\r
117 tutorialact = QtGui.QAction(u"ヘルプの表示(&H)", self)
\r
118 tutorialact.triggered.connect(self.tutorial_triggered)
\r
119 tutorialact.setShortcut(QtGui.QKeySequence.HelpContents)
\r
120 helpmenu.addAction(tutorialact)
\r
121 projectact = QtGui.QAction(u"プロジェクトのWebサイト(&W)", self)
\r
122 projectact.triggered.connect(self.project_triggered)
\r
123 helpmenu.addAction(projectact)
\r
124 aboutact = QtGui.QAction(u"Pydunについて(&A)...", self)
\r
125 aboutact.triggered.connect(self.about_triggered)
\r
126 helpmenu.addAction(aboutact)
\r
128 @QtCore.Slot(bool, bool)
\r
129 def updateundostate(self, canundo, canredo):
\r
131 self.undoact.setEnabled(True)
\r
133 self.undoact.setDisabled(True)
\r
135 self.redoact.setEnabled(True)
\r
137 self.redoact.setDisabled(True)
\r
139 def setTitle(self, filename):
\r
140 s = self.getfilename(filename) + " - Pydun"
\r
141 self.setWindowTitle(s)
\r
143 def getfilename(self, filename):
\r
144 if filename == None:
\r
147 s = os.path.splitext(os.path.basename(filename))[0]
\r
151 def new_triggered(self):
\r
152 if self.confirmdiscarding():
\r
159 _mapengine = MapEngine(20, 20, 1, -1, 0, +19)
\r
160 _undomanager.init(_mapengine.savestring())
\r
161 self.setTitle(None)
\r
163 self.mainframe.mapframe.repaint()
\r
167 def confirmdiscarding(self):
\r
168 if not _undomanager.commited:
\r
169 dlg = PydunAskSaveDialog(self, self.getfilename(_mapengine.filename))
\r
171 if ret == QtGui.QMessageBox.Cancel:
\r
173 elif ret == QtGui.QMessageBox.Save:
\r
174 saved = self.save_triggered()
\r
180 def open_triggered(self):
\r
181 if self.confirmdiscarding():
\r
184 d = os.path.dirname(_mapengine.filename)
\r
187 filename = QtGui.QFileDialog.getOpenFileName(
\r
189 filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")
\r
190 if filename[0] != u"":
\r
191 self.open(filename[0])
\r
193 def open(self, filename):
\r
194 _mapengine.load(filename)
\r
195 _undomanager.init(_mapengine.savestring())
\r
196 self.setTitle(_mapengine.filename)
\r
198 self.mainframe.mapframe.repaint()
\r
203 def save_triggered(self):
\r
204 if _mapengine.filename:
\r
205 self.save(_mapengine.filename)
\r
208 saved = self.saveas_triggered()
\r
212 def saveas_triggered(self):
\r
215 d = os.path.dirname(_mapengine.filename)
\r
218 filename = QtGui.QFileDialog.getSaveFileName(
\r
220 filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")
\r
221 if filename[0] != u"":
\r
222 self.save(filename[0])
\r
227 def save(self, filename):
\r
228 _mapengine.save(filename)
\r
229 _undomanager.commit()
\r
230 self.setTitle(_mapengine.filename)
\r
233 def exit_triggered(self):
\r
236 def closeEvent(self, event):
\r
244 global configfilename
\r
245 if self.confirmdiscarding():
\r
246 config["windowSize"] = dict()
\r
247 config["windowSize"]["width"] = self.size().width()
\r
248 config["windowSize"]["height"] = self.size().height()
\r
249 with open(configfilename, "w") as f:
\r
250 yaml.safe_dump(config, f, default_flow_style=False)
\r
256 def undo_triggered(self):
\r
258 _mapengine.loadfromstring(_undomanager.undo())
\r
259 self.mainframe.mapframe.repaint()
\r
262 def redo_triggered(self):
\r
264 _mapengine.loadfromstring(_undomanager.redo())
\r
265 self.mainframe.mapframe.repaint()
\r
268 def setorigine_triggered(self):
\r
270 if self.mainframe.mapframe.setoriginemode:
\r
271 QtGui.QMessageBox.information(
\r
272 self, title, u"座標設定を中止します。", QtGui.QMessageBox.Ok)
\r
273 self.mainframe.mapframe.setoriginemode = False
\r
275 if QtGui.QMessageBox.Ok == QtGui.QMessageBox.information(
\r
276 self, title, u"基準にする地点をクリックしてください。",
\r
277 (QtGui.QMessageBox.Ok| QtGui.QMessageBox.Cancel)):
\r
278 self.mainframe.mapframe.setoriginemode = True
\r
281 def setmapsize_triggered(self):
\r
282 dlg = SetSizeDialog(self)
\r
283 dlg.setoriginalsize(_mapengine.width, _mapengine.height)
\r
285 if dlg.result() == QtGui.QDialog.Accepted:
\r
286 top, bottom, left, right = dlg.getsize()
\r
287 _mapengine.changesize(top, bottom, left, right)
\r
288 _undomanager.save(_mapengine.savestring())
\r
289 self.mainframe.mapframe.repaint()
\r
292 def togglewallmenustring_triggered(self):
\r
294 config["showWallMenuString"] = not config.get("showWallMenuString", False)
\r
295 QtGui.QMessageBox.information(
\r
296 self, u"壁メニューに文字を表示する", u"表示の切替は再起動後に有効になります。",
\r
297 (QtGui.QMessageBox.Ok))
\r
300 def tutorial_triggered(self):
\r
301 url = basedir() + "/help/index.html"
\r
302 webbrowser.open_new_tab(url)
\r
305 def project_triggered(self):
\r
306 webbrowser.open_new_tab(projecturl)
\r
309 def about_triggered(self):
\r
310 QtGui.QMessageBox.about(self, "Pydun",
\r
311 u"<h1>Pydun.py "+ projectversion + "</h1>"
\r
312 u"<p>Copyright (c) 2013 WATAHIKI Hiroyuki</p>"
\r
313 u"<p>url: <a href='" + projecturl + "'>" + projecturl + "</a></p>"
\r
314 u"<p>e-mail: hrwatahiki at gmail.com</p>"
\r
315 u"<p>twitter: <a href='https://twitter.com/hrwatahiki'>@hrwatahiki</a></p>"
\r
316 u"<p>blog: <a href='http://hrwatahiki.blogspot.jp/'>作業記録</a></p>"
\r
317 u"<p>このソフトウェアはMITライセンスです。</p>"
\r
318 u"<p>このソフトウェアは以下のソフトウェアを使用しています。: "
\r
319 u"Python, PySide, PyYAML "
\r
320 u"これらの作成者に深く感謝いたします。</p>"
\r
321 u"<p>詳細はLICENCE.txtを参照してください。</p>")
\r
324 class MainFrame(QtGui.QFrame):
\r
325 create_wall_menu_triggered_signal = QtCore.Signal(int, int, str, int)
\r
327 def __init__(self, parent=None):
\r
328 super(MainFrame, self).__init__(parent)
\r
330 self.mapframe = MapFrame(self)
\r
331 scrollarea = QtGui.QScrollArea(self)
\r
332 scrollarea.setWidget(self.mapframe)
\r
334 self.detail = QtGui.QLabel(self)
\r
335 self.detail.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
336 self.detail.setText(u"")
\r
337 self.detail.setMaximumHeight(100)
\r
338 self.detail.setMinimumHeight(100)
\r
340 self.boxdrawbutton = QtGui.QRadioButton(self)
\r
341 self.boxdrawbutton.setText(u"ボックス形式で壁を描画(&B)")
\r
342 self.boxdrawbutton.setChecked(True)
\r
343 self.boxdrawbutton.setSizePolicy(
\r
344 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
346 self.growdrawbutton = QtGui.QRadioButton(self)
\r
347 self.growdrawbutton.setText(u"足跡形式で壁を描画(&G)")
\r
348 self.growdrawbutton.setChecked(False)
\r
349 self.growdrawbutton.setSizePolicy(
\r
350 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
352 self.backcolorbutton = QtGui.QRadioButton(self)
\r
353 self.backcolorbutton.setText(u"背景色(&C)")
\r
354 self.backcolorbutton.setChecked(False)
\r
355 self.backcolorbutton.setSizePolicy(
\r
356 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
358 self.setbackcolorbutton = QtGui.QPushButton(self)
\r
359 self.setbackcolorbutton.setText(u"背景色を設定(&S)...")
\r
360 self.setbackcolorbutton.setSizePolicy(
\r
361 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
363 self.backcolorbox = ColorBox(self)
\r
364 self.backcolorbox.setMinimumSize(30, 30)
\r
365 self.backcolorbox.setSizePolicy(
\r
366 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
368 latestversion = getlatestversion()
\r
369 if latestversion != projectversion:
\r
370 self.update = QtGui.QLabel(self)
\r
371 self.update.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
372 self.update.setText(u"<a href='{url}'>最新のPydun({ver})がダウンロードできます。</a>".format(url=projecturl, ver=latestversion))
\r
373 self.update.setOpenExternalLinks(True)
\r
375 layout = QtGui.QGridLayout(self)
\r
376 layout.addWidget(scrollarea, 0, 0, 1, 3)
\r
377 layout.addWidget(self.detail, 1, 0, 4, 1)
\r
378 layout.addWidget(self.boxdrawbutton, 1, 1, 1, 2)
\r
379 layout.addWidget(self.growdrawbutton, 2, 1, 1, 2)
\r
380 layout.addWidget(self.backcolorbutton, 3, 1, 1, 2)
\r
381 layout.addWidget(self.setbackcolorbutton, 4, 1, 1, 1)
\r
382 layout.addWidget(self.backcolorbox, 4, 2, 1, 1)
\r
383 if latestversion != projectversion:
\r
384 layout.addWidget(self.update, 5, 0, 1, 3)
\r
386 self.setLayout(layout)
\r
388 self.h_wall_menu = self.create_wall_menu("h")
\r
389 self.v_wall_menu = self.create_wall_menu("v")
\r
391 self.mapframe.mouse_moved.connect(self.mouse_moved)
\r
392 self.mapframe.mouse_released.connect(self.mouse_released)
\r
393 self.mapframe.mouse_drag_released.connect(self.mouse_drag_released)
\r
394 self.create_wall_menu_triggered_signal.connect(
\r
395 self.create_wall_menu_triggered)
\r
396 self.setbackcolorbutton.clicked.connect(
\r
397 self.setbackcolorbutton_clicked)
\r
399 def create_wall_menu(self, direction):
\r
400 menu = QtGui.QMenu(self)
\r
401 for idx, img in enumerate(_mapimages.wall_icons):
\r
402 act = QtGui.QAction(_mapimages.wall_texts[idx][direction], self)
\r
403 act.setIcon(img[direction])
\r
407 self.create_wall_menu_triggered_signal.emit(menu.x, menu.y, direction, idx)
\r
410 act.triggered.connect(triggerd(idx))
\r
411 menu.addAction(act)
\r
414 @QtCore.Slot(int, int, int)
\r
415 def mouse_moved(self, x=0, y=0, b=QtCore.Qt.MouseButton.NoButton):
\r
416 cood = u"({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))
\r
417 self.detail.setText(cood + _mapengine.getdetail(x, y))
\r
418 self.mapframe.repaint()
\r
420 @QtCore.Slot(int, int, int, int, int)
\r
421 def mouse_drag_released(self, x1, y1, x2, y2, eraseonly):
\r
422 if self.boxdrawbutton.isChecked():
\r
423 _mapengine.growwall(x1, y1, x2, y2, eraseonly, True)
\r
424 elif self.growdrawbutton.isChecked():
\r
425 _mapengine.growwall(x1, y1, x2, y2, eraseonly, False)
\r
426 elif self.backcolorbutton.isChecked():
\r
430 backcolor = getcolorstring(self.backcolorbox.color)
\r
431 _mapengine.fillbackcolor(x1, y1, x2, y2, backcolor)
\r
432 _undomanager.save(_mapengine.savestring())
\r
433 self.mapframe.repaint()
\r
435 @QtCore.Slot(int, int, str)
\r
436 def mouse_released(self, x1, y1, direction):
\r
438 if self.mapframe.setoriginemode:
\r
439 dlg = SetOrigineDialog(self)
\r
440 dlg.setcurrent(_mapengine.viewx(x1), _mapengine.viewy(y1))
\r
441 dlg.exec_() #showでは処理がとまらない。
\r
442 if dlg.result() == QtGui.QDialog.Accepted:
\r
443 _mapengine.setoffset(
\r
444 dlg.originex - _mapengine.viewx(x1) + _mapengine.offsetx,
\r
445 dlg.originey - _mapengine.viewy(y1) + _mapengine.offsety
\r
447 _undomanager.save(_mapengine.savestring())
\r
448 self.mapframe.setoriginemode = False
\r
451 if direction == "c":
\r
452 dlg = DetailDialog(self)
\r
453 dlg.setvalue(_mapengine.viewx(x1), _mapengine.viewy(y1),
\r
454 _mapengine.getmark(x1, y1), _mapengine.getdetail(x1, y1),
\r
455 getcolorfromstring(_mapengine.getforecolor(x1, y1)))
\r
456 dlg.exec_() #showでは処理がとまらない。
\r
457 if dlg.result() == QtGui.QDialog.Accepted:
\r
458 forecolor = getcolorstring(dlg.forecolorbox.color)
\r
459 _mapengine.setmark(x1, y1, dlg.marktext.text())
\r
460 _mapengine.setdetail(x1, y1, dlg.detailtext.toPlainText())
\r
461 _mapengine.setforecolor(x1, y1, forecolor)
\r
462 _undomanager.save(_mapengine.savestring())
\r
463 self.mapframe.repaint()
\r
467 if direction == "h":
\r
468 menu = self.h_wall_menu
\r
469 elif direction == "v":
\r
470 menu = self.v_wall_menu
\r
473 menu.popup(QtGui.QCursor.pos())
\r
475 @QtCore.Slot(int, int, str, int)
\r
476 def create_wall_menu_triggered(self, x1, y1, direction, wall):
\r
477 _mapengine.setdata(x1, y1, direction, wall)
\r
478 _undomanager.save(_mapengine.savestring())
\r
479 self.mapframe.repaint()
\r
482 def setbackcolorbutton_clicked(self):
\r
484 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
485 dlg.setCurrentColor(self.backcolorbox.color)
\r
487 config["customColor"] = dlg.config
\r
488 if dlg.result() == QtGui.QDialog.Accepted:
\r
489 self.backcolorbox.color = dlg.currentColor()
\r
490 self.backcolorbutton.setChecked(True)
\r
493 class MapFrame(QtGui.QFrame):
\r
494 mouse_moved = QtCore.Signal(int, int, int)
\r
495 mouse_released = QtCore.Signal(int, int, str)
\r
496 mouse_drag_released = QtCore.Signal(int, int, int, int, int)
\r
500 def __init__(self, parent=None):
\r
501 super(MapFrame, self).__init__(parent)
\r
502 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
511 self._dragging = False
\r
512 self.setoriginemode = False
\r
513 self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
515 _mapimages.width * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
516 _mapimages.height * (_mapengine.height) + _mapimages.heightoffset * 2
\r
519 def paintEvent(self, event):
\r
520 painter = QtGui.QPainter(self)
\r
521 painter.fillRect(0, 0, self.width(), self.height(), QtGui.QColor(255, 255, 255))
\r
522 w = _mapimages.width - 1
\r
523 v = _mapimages.height - 1
\r
524 ho = _mapimages.heightoffset
\r
525 wo = _mapimages.widthoffset
\r
529 w * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
530 v * (_mapengine.height) + _mapimages.heightoffset * 2
\r
534 for x in range(_mapengine.width):
\r
536 for y in range(_mapengine.height):
\r
538 backcolor = _mapengine.getbackcolor(x, y)
\r
540 painter.fillRect(wo + xx, ho + yy, w, v,
\r
541 getcolorfromstring(backcolor))
\r
544 for x in range(_mapengine.width + 1):
\r
546 for y in range(_mapengine.height + 1):
\r
548 if x != _mapengine.width:
\r
549 painter.drawImage(wo + xx, yy,
\r
550 _mapimages.wall(0, "h"))
\r
551 if y != _mapengine.height:
\r
552 painter.drawImage(xx, ho + yy,
\r
553 _mapimages.wall(0, "v"))
\r
556 for x in range(_mapengine.width + 1):
\r
558 for y in range(_mapengine.height + 1):
\r
560 if x != _mapengine.width and _mapengine.getdata(x, y, "h") != 0:
\r
561 painter.drawImage(wo + xx, yy,
\r
562 _mapimages.wall(_mapengine.getdata(x, y, "h"), "h"))
\r
563 if y != _mapengine.height and _mapengine.getdata(x, y, "v") != 0:
\r
564 painter.drawImage(xx, ho + yy,
\r
565 _mapimages.wall(_mapengine.getdata(x, y, "v"), "v"))
\r
566 mark = _mapengine.getmark(x, y)
\r
568 painter.setPen(getcolorfromstring(_mapengine.getforecolor(x, y)))
\r
569 painter.drawText(wo + xx + 2, ho + yy + 2, w - 2, v - 2,
\r
570 QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter,
\r
573 #座標設定中はdrawing box を表示しない。
\r
574 if self.setoriginemode:
\r
578 if self._pressedbutton != QtCore.Qt.MouseButton.NoButton:
\r
579 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
580 if self._x1 == self._x2 and self._y1 == self._y2:
\r
581 painter.setPen(QtGui.QColor(255, 0, 0))
\r
582 elif self._x1 == self._x2 or self._y1 == self._y2:
\r
583 painter.setPen(QtGui.QColor(0, 255, 0))
\r
585 painter.setPen(QtGui.QColor(255, 0, 0))
\r
586 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
587 painter.setPen(QtGui.QColor(0, 0, 255))
\r
588 painter.drawRect(self._px1, self._py1,
\r
589 self._px2 - self._px1, self._py2 - self._py1)
\r
591 def eventFilter(self, obj, event):
\r
593 return ((event.pos().x() - _mapimages.widthoffset) // (_mapimages.width - 1))
\r
596 return ((event.pos().y() - _mapimages.heightoffset) // (_mapimages.height - 1))
\r
601 if et == QtCore.QEvent.MouseButtonPress:
\r
604 self._pos1 = event.pos()
\r
605 self._px1 = event.pos().x()
\r
606 self._py1 = event.pos().y()
\r
609 self._px2 = event.pos().x()
\r
610 self._py2 = event.pos().y()
\r
611 self._pressedbutton = event.buttons()
\r
612 self._dragging = False
\r
615 elif et == QtCore.QEvent.MouseMove:
\r
618 self._px2 = event.pos().x()
\r
619 self._py2 = event.pos().y()
\r
620 if (self._pressedbutton != QtCore.Qt.MouseButton.NoButton and
\r
621 (event.pos() - self._pos1).manhattanLength() >=
\r
622 QtGui.QApplication.startDragDistance()):
\r
623 self._dragging = True
\r
624 self.mouse_moved.emit(self._x2, self._y2, event.buttons())
\r
627 elif et == QtCore.QEvent.MouseButtonRelease:
\r
629 release_emit = False
\r
632 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
634 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
637 release_emit = True
\r
638 if self.setoriginemode:
\r
639 release_emit = True
\r
641 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
642 self._dragging = False
\r
644 self.mouse_drag_released.emit(
\r
645 self._x1, self._y1, self._x2, self._y2, eraseonly)
\r
647 rpx = self._px2 - self._x2 * (_mapimages.width - 1) - _mapimages.widthoffset
\r
648 rpy = self._py2 - self._y2 * (_mapimages.height - 1) - _mapimages.heightoffset
\r
649 rdx = rpx - (_mapimages.width - 1) // 2
\r
650 rdy = rpy - (_mapimages.height - 1) // 2
\r
651 if rpx <= _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
655 elif rpx >= _mapimages.width - _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
659 elif rpy <= _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
663 elif rpy >= _mapimages.height - _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
671 self.mouse_released.emit(rx, ry, d)
\r
677 # pass the event on to the parent class
\r
681 class ColorBox(QtGui.QFrame):
\r
682 def __init__(self, parent=None):
\r
683 super(ColorBox, self).__init__(parent)
\r
684 self.color = QtGui.QColor(255, 255, 255)
\r
685 self.bordercolor = QtGui.QColor(0, 0, 0)
\r
687 def paintEvent(self, event):
\r
688 painter = QtGui.QPainter(self)
\r
689 painter.fillRect(0, 0, self.width(), self.height(), self.color)
\r
690 painter.setPen(self.bordercolor)
\r
691 painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
\r
694 class DetailDialog(QtGui.QDialog):
\r
695 def __init__(self, parent=None):
\r
696 super(DetailDialog, self).__init__(parent)
\r
698 marklabel = QtGui.QLabel(self)
\r
699 marklabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
700 marklabel.setText(u"マーク(&M)")
\r
701 marklabel.setSizePolicy(
\r
702 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
704 self.marktext = QtGui.QLineEdit(self)
\r
705 self.marktext.setMaxLength(1)
\r
706 self.marktext.setText(u"")
\r
707 self.marktext.setMinimumWidth(20)
\r
708 self.marktext.setSizePolicy(
\r
709 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
710 marklabel.setBuddy(self.marktext)
\r
712 self.forecolorbutton = QtGui.QPushButton(self)
\r
713 self.forecolorbutton.setText(u"文字色(&C)...")
\r
714 self.forecolorbutton.clicked.connect(self.forecolorbutton_clicked)
\r
716 self.forecolorbox = ColorBox(self)
\r
717 self.forecolorbox.setMinimumSize(30, 30)
\r
718 self.forecolorbox.setSizePolicy(
\r
719 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
721 detaillabel = QtGui.QLabel(self)
\r
722 detaillabel.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight)
\r
723 detaillabel.setText(u"詳細(&D)")
\r
725 self.detailtext = QtGui.QTextEdit(self)
\r
726 self.detailtext.setText(u"")
\r
727 detaillabel.setBuddy(self.detailtext)
\r
729 self.buttonbox = QtGui.QDialogButtonBox(
\r
730 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
731 self.buttonbox.accepted.connect(self.accept)
\r
732 self.buttonbox.rejected.connect(self.reject)
\r
733 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
734 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
736 layout = QtGui.QGridLayout()
\r
737 layout.addWidget(marklabel, 0, 0, 1, 1)
\r
738 layout.addWidget(self.marktext, 0, 1, 1, 1)
\r
739 layout.addWidget(self.forecolorbutton, 0, 2, 1, 1)
\r
740 layout.addWidget(self.forecolorbox, 0, 3, 1, 1)
\r
741 layout.addWidget(detaillabel, 1, 0, 1, 1)
\r
742 layout.addWidget(self.detailtext, 1, 1, 1, 3)
\r
743 layout.addWidget(self.buttonbox, 2, 0, 1, 4)
\r
744 self.setLayout(layout)
\r
745 self.setModal(True)
\r
747 def setvalue(self, x, y, mark, detail, color):
\r
748 self.setWindowTitle("({x}, {y})".format(x=x, y=y))
\r
749 self.marktext.setText(mark)
\r
750 self.detailtext.setText(detail)
\r
751 self.forecolorbox.color = color
\r
753 def forecolorbutton_clicked(self):
\r
755 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
756 dlg.setCurrentColor(self.forecolorbox.color)
\r
758 config["customColor"] = dlg.config
\r
759 if dlg.result() == QtGui.QDialog.Accepted:
\r
760 self.forecolorbox.color = dlg.currentColor()
\r
763 class SetOrigineDialog(QtGui.QDialog):
\r
764 def __init__(self, parent=None):
\r
765 super(SetOrigineDialog, self).__init__(parent)
\r
766 self.setWindowTitle(u"座標設定")
\r
768 promptlabel = QtGui.QLabel(self)
\r
769 promptlabel.setAlignment(
\r
770 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
771 promptlabel.setText(u"この地点の座標を入力してください。")
\r
773 self.currentlabel = QtGui.QLabel(self)
\r
774 self.currentlabel.setAlignment(
\r
775 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
\r
776 self.currentlabel.setText(u"")
\r
778 xlabel = QtGui.QLabel(self)
\r
779 xlabel.setAlignment(
\r
780 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
781 xlabel.setText(u"&X")
\r
783 self.xbox = QtGui.QSpinBox(self)
\r
784 self.xbox.setRange(-999, +999)
\r
785 self.xbox.setSingleStep(1)
\r
786 self.xbox.setValue(0)
\r
787 xlabel.setBuddy(self.xbox)
\r
789 ylabel = QtGui.QLabel(self)
\r
790 ylabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
791 ylabel.setText(u"&Y")
\r
793 self.ybox = QtGui.QSpinBox(self)
\r
794 self.ybox.setRange(-999, +999)
\r
795 self.ybox.setSingleStep(1)
\r
796 self.ybox.setValue(0)
\r
797 ylabel.setBuddy(self.ybox)
\r
799 self.buttonbox = QtGui.QDialogButtonBox(
\r
800 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
801 self.buttonbox.accepted.connect(self.accept)
\r
802 self.buttonbox.rejected.connect(self.reject)
\r
803 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
804 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
806 layout = QtGui.QGridLayout()
\r
807 layout.addWidget(promptlabel, 0, 0, 1, 4)
\r
808 layout.addWidget(self.currentlabel, 1, 0, 1, 4)
\r
809 layout.addWidget(xlabel, 2, 0, 1, 1)
\r
810 layout.addWidget(self.xbox, 2, 1, 1, 1)
\r
811 layout.addWidget(ylabel, 2, 2, 1, 1)
\r
812 layout.addWidget(self.ybox, 2, 3, 1, 1)
\r
813 layout.addWidget(self.buttonbox, 3, 0, 1, 4)
\r
814 self.setLayout(layout)
\r
815 self.setModal(True)
\r
817 def setcurrent(self, x, y):
\r
818 self.xbox.setValue(x)
\r
819 self.ybox.setValue(y)
\r
820 self.currentlabel.setText(u"現在の座標 ({x}, {y})".format(x=x, y=y))
\r
823 def originex(self):
\r
824 return self.xbox.value()
\r
827 def originey(self):
\r
828 return self.ybox.value()
\r
831 class SetSizeDialog(QtGui.QDialog):
\r
832 def __init__(self, parent=None):
\r
833 super(SetSizeDialog, self).__init__(parent)
\r
834 self.setWindowTitle(u"マップのサイズ")
\r
836 self.topbutton = QtGui.QRadioButton(self)
\r
837 self.topbutton.setText(u"上(&T)")
\r
838 self.topbutton.clicked.connect(self.updatewidgets)
\r
840 self.topsize = QtGui.QSpinBox(self)
\r
841 self.topsize.setSingleStep(1)
\r
842 self.topsize.setValue(0)
\r
843 self.topsize.valueChanged.connect(self.updatewidgets)
\r
845 self.bottombutton = QtGui.QRadioButton(self)
\r
846 self.bottombutton.setText(u"下(&B)")
\r
847 self.bottombutton.clicked.connect(self.updatewidgets)
\r
849 self.bottomsize = QtGui.QSpinBox(self)
\r
850 self.bottomsize.setSingleStep(1)
\r
851 self.bottomsize.setValue(0)
\r
852 self.bottomsize.valueChanged.connect(self.updatewidgets)
\r
854 self.leftbutton = QtGui.QRadioButton(self)
\r
855 self.leftbutton.setText(u"左(&L)")
\r
856 self.leftbutton.clicked.connect(self.updatewidgets)
\r
858 self.leftsize = QtGui.QSpinBox(self)
\r
859 self.leftsize.setSingleStep(1)
\r
860 self.leftsize.setValue(0)
\r
861 self.leftsize.valueChanged.connect(self.updatewidgets)
\r
863 self.rightbutton = QtGui.QRadioButton(self)
\r
864 self.rightbutton.setText(u"右(&R)")
\r
865 self.rightbutton.clicked.connect(self.updatewidgets)
\r
867 self.rightsize = QtGui.QSpinBox(self)
\r
868 self.rightsize.setSingleStep(1)
\r
869 self.rightsize.setValue(0)
\r
870 self.rightsize.valueChanged.connect(self.updatewidgets)
\r
872 self.sizelabel = QtGui.QLabel(self)
\r
873 self.sizelabel .setAlignment(
\r
874 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
875 self.sizelabel.setText(u"この地点の座標を入力してください。")
\r
877 self.buttonbox = QtGui.QDialogButtonBox(
\r
878 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
879 self.buttonbox.accepted.connect(self.accept)
\r
880 self.buttonbox.rejected.connect(self.reject)
\r
881 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText(u"OK")
\r
882 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText(u"キャンセル")
\r
884 verticalgroup = QtGui.QButtonGroup(self)
\r
885 verticalgroup.addButton(self.topbutton)
\r
886 verticalgroup.addButton(self.bottombutton)
\r
888 holizontalgroup = QtGui.QButtonGroup(self)
\r
889 holizontalgroup.addButton(self.leftbutton)
\r
890 holizontalgroup.addButton(self.rightbutton)
\r
892 self.topbutton.setChecked(True)
\r
893 self.bottombutton.setChecked(False)
\r
894 self.leftbutton.setChecked(True)
\r
895 self.rightbutton.setChecked(False)
\r
897 layout = QtGui.QGridLayout(self)
\r
898 layout.addWidget(self.topbutton, 0, 2, 1, 1)
\r
899 layout.addWidget(self.topsize, 0, 3, 1, 1)
\r
900 layout.addWidget(self.leftbutton, 1, 0, 1, 1)
\r
901 layout.addWidget(self.leftsize, 1, 1, 1, 1)
\r
902 layout.addWidget(self.sizelabel, 1, 2, 1, 2)
\r
903 layout.addWidget(self.rightbutton, 1, 4, 1, 1)
\r
904 layout.addWidget(self.rightsize, 1, 5, 1, 1)
\r
905 layout.addWidget(self.bottombutton, 2, 2, 1, 1)
\r
906 layout.addWidget(self.bottomsize, 2, 3, 1, 1)
\r
907 layout.addWidget(self.buttonbox, 3, 0, 1, 6)
\r
908 self.setLayout(layout)
\r
909 self.setModal(True)
\r
911 def setoriginalsize(self, width, height):
\r
912 self._width = width
\r
913 self._height = height
\r
914 self.topsize.setRange(-height+1, +100)
\r
915 self.bottomsize.setRange(-height+1, +100)
\r
916 self.leftsize.setRange(-width+1, +100)
\r
917 self.rightsize.setRange(-width+1, +100)
\r
918 self.updatewidgets()
\r
920 def updatewidgets(self):
\r
924 if self.topbutton.isChecked():
\r
925 dh = self.topsize.value()
\r
926 self.topsize.setEnabled(True)
\r
927 self.bottomsize.setDisabled(True)
\r
928 elif self.bottombutton.isChecked():
\r
929 dh = self.bottomsize.value()
\r
930 self.topsize.setDisabled(True)
\r
931 self.bottomsize.setEnabled(True)
\r
932 if self.leftbutton.isChecked():
\r
933 dw = self.leftsize.value()
\r
934 self.leftsize.setEnabled(True)
\r
935 self.rightsize.setDisabled(True)
\r
936 elif self.rightbutton.isChecked():
\r
937 dw = self.rightsize.value()
\r
938 self.leftsize.setDisabled(True)
\r
939 self.rightsize.setEnabled(True)
\r
941 self.sizelabel.setText(
\r
942 u"変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(
\r
943 w1=self._width, h1=self._height,
\r
944 w2=self._width+dw, h2=self._height+dh))
\r
951 if self.topbutton.isChecked():
\r
952 top = self.topsize.value()
\r
953 elif self.bottombutton.isChecked():
\r
954 bottom = self.bottomsize.value()
\r
955 if self.leftbutton.isChecked():
\r
956 left = self.leftsize.value()
\r
957 elif self.rightbutton.isChecked():
\r
958 right = self.rightsize.value()
\r
959 return (top, bottom, left, right)
\r
962 class MapImages(object):
\r
963 def __init__(self, show_wall_menu_string):
\r
964 if show_wall_menu_string:
\r
965 vtext = [u"なし", u"壁", u"扉", u"扉(→)", u"扉(←)", u"一通(→)", u"一通(←)", u"隠", u"隠(→)", u"隠(←)",]
\r
966 htext = [u"なし", u"壁", u"扉", u"扉(↓)", u"扉(↑)", u"一通(↓)", u"一通(↑)", u"隠", u"隠(↓)", u"隠(↑)",]
\r
968 vtext = [u"", u"", u"", u"", u"", u"", u"", u"", u"", u"",]
\r
969 htext = [u"", u"", u"", u"", u"", u"", u"", u"", u"", u"",]
\r
970 self.wall_images = list()
\r
971 self.wall_icons = list()
\r
972 self.wall_texts = list()
\r
973 for index in range(10):
\r
974 self.wall_images.append(dict())
\r
975 self.wall_icons.append(dict())
\r
976 self.wall_texts.append(dict())
\r
977 for direction in ["v", "h"]:
\r
978 filename = os.path.join(
\r
981 u"wall_{direction}_{index:02}.png".format(
\r
982 direction=direction, index=index))
\r
983 self.wall_images[index][direction] = QtGui.QImage()
\r
984 self.wall_images[index][direction].load(filename)
\r
985 self.wall_icons[index][direction] = QtGui.QIcon(filename)
\r
986 self.wall_texts[index]["v"] = vtext[index]
\r
987 self.wall_texts[index]["h"] = htext[index]
\r
991 return self.wall_images[0]["h"].width()
\r
995 return self.wall_images[0]["v"].height()
\r
998 def widthoffset(self):
\r
999 return self.wall_images[0]["v"].width()//2
\r
1002 def heightoffset(self):
\r
1003 return self.wall_images[0]["h"].height()//2
\r
1005 def wall(self, index, direction):
\r
1006 return self.wall_images[index][direction]
\r
1009 class MapEngine(object):
\r
1010 hwall = " -#WMwmHVA"
\r
1011 vwall = " |#PCpc=DG"
\r
1013 def __init__(self, width, height, signx, signy, offsetx, offsety):
\r
1014 self._width = width
\r
1015 self._height = height
\r
1016 self._signx = signx
\r
1017 self._signy = signy
\r
1018 self._offsetx = offsetx
\r
1019 self._offsety = offsety
\r
1020 self.filename = None
\r
1024 self._note = dict()
\r
1026 def initdata(self):
\r
1027 width = self.width + 1
\r
1028 height = self.height + 1
\r
1029 self._data = self.initialdata(width, height)
\r
1031 def initialdata(self, width, height):
\r
1033 for x in range(width):
\r
1035 for y in range(height):
\r
1036 dt[x].append(dict())
\r
1037 for d in ["h", "v"]:
\r
1041 def inityaml(self):
\r
1042 #yaml !python/Unicode出力抑止おまじない
\r
1043 def represent_unicode(dumper, data):
\r
1044 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
\r
1045 def construct_unicode(loader, node):
\r
1046 return unicode(loader.construct_scalar(node))
\r
1047 yaml.add_representer(unicode, represent_unicode)
\r
1048 yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)
\r
1050 def getdata(self, x, y, direction):
\r
1051 return self._data[x][y][direction]
\r
1053 def setdata(self, x, y, direction, value):
\r
1054 self._data[x][y][direction] = value
\r
1058 return self._width
\r
1062 return self._height
\r
1066 return self._signx
\r
1070 return self._signy
\r
1073 def offsetx(self):
\r
1074 return self._offsetx
\r
1077 def offsety(self):
\r
1078 return self._offsety
\r
1080 def setoffset(self, x, y):
\r
1084 def getmark(self, x, y):
\r
1085 return self.unescape(self.getnote(x, y)["mark"])
\r
1087 def getdetail(self, x, y):
\r
1088 return self.unescape(self.getnote(x, y)["detail"])
\r
1090 def getforecolor(self, x, y):
\r
1091 return self.getnote(x, y)["forecolor"]
\r
1093 def getbackcolor(self, x, y):
\r
1094 return self.getnote(x, y)["backcolor"]
\r
1096 def getnote(self, x, y):
\r
1097 return self._note.get(
\r
1098 self.coodtokey(x, y), {"mark":u"", "detail":u"", "forecolor":u"#000000", "backcolor":u""})
\r
1100 def coodtokey(self, x, y):
\r
1101 return u"{x:+05d}_{y:+05d}".format(x=x, y=y)
\r
1103 def keytocood(self, key):
\r
1104 return map(int, key.split("_"))
\r
1106 def setmark(self, x, y, mark):
\r
1107 note = self.getnote(x, y)
\r
1108 note["mark"] = self.escape(mark)
\r
1109 self.setnote(x, y, note)
\r
1111 def setdetail(self, x, y, detail):
\r
1112 note = self.getnote(x, y)
\r
1113 note["detail"] = self.escape(detail)
\r
1114 self.setnote(x, y, note)
\r
1116 def setforecolor(self, x, y, color):
\r
1117 note = self.getnote(x, y)
\r
1118 note["forecolor"] = color
\r
1119 self.setnote(x, y, note)
\r
1121 def setbackcolor(self, x, y, color):
\r
1122 note = self.getnote(x, y)
\r
1123 note["backcolor"] = color
\r
1124 self.setnote(x, y, note)
\r
1126 def setnote(self, x, y, note):
\r
1127 self._note[self.coodtokey(x, y)] = note
\r
1129 def escape(self, s):
\r
1130 return s.replace("\\", "\\\\").replace("\n", r"\n")
\r
1132 def unescape(self, s):
\r
1133 return s.replace(r"\n", "\n").replace("\\\\", "\\")
\r
1135 def viewx(self, x):
\r
1136 return x * self.signx + self.offsetx
\r
1138 def viewy(self, y):
\r
1139 return y * self.signy + self.offsety
\r
1141 def worldx(self, x):
\r
1142 return (x - self.offsetx) / self.signx
\r
1144 def worldy(self, y):
\r
1145 return (y - self.offsety) / self.signy
\r
1147 def changesize(self, top, bottom, left, right):
\r
1148 oldoffsetx = max(-left, 0)
\r
1149 newoffsetx = max(left, 0)
\r
1150 newwidth = self.width + left + right
\r
1151 oldoffsety = max(-top, 0)
\r
1152 newoffsety = max(top, 0)
\r
1153 newheight = self.height + top + bottom
\r
1155 newdata = self.initialdata(newwidth + 1, newheight + 1)
\r
1157 for x in range(min(self._width, newwidth) + 1):
\r
1158 for y in range(min(self._height, newheight) + 1):
\r
1159 for d in ["h", "v"]:
\r
1160 newdata[x+newoffsetx][y+newoffsety][d] = self._data[x+oldoffsetx][y+oldoffsety][d]
\r
1161 newnote[self.coodtokey(x+newoffsetx, y+newoffsety)] = self.getnote(x+oldoffsetx, y+oldoffsety)
\r
1162 self._width = newwidth
\r
1163 self._height = newheight
\r
1164 self.setoffset(self.offsetx -self.signx * left, self.offsety -self.signy * top)
\r
1165 self._data = newdata
\r
1166 self._note = newnote
\r
1168 def growwall(self, x1, y1, x2, y2, eraseonly, alwaysbox):
\r
1169 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1170 offsetx, offsety = self.getoffset(x1, y1, x2, y2)
\r
1172 #delete inner walls.
\r
1173 for x in range(x1, x2+stepx, stepx):
\r
1174 for y in range(y1+stepy+offsety, y2+stepy+offsety, stepy):
\r
1175 self._data[x][y]["h"] = 0
\r
1176 for x in range(x1+stepx+offsetx, x2+stepx+offsetx, stepx):
\r
1177 for y in range(y1, y2+stepy, stepy):
\r
1178 self._data[x][y]["v"] = 0
\r
1181 #draw OUTER wall if it exists.
\r
1182 if alwaysbox or (x1 == x2 and y1 == y2):
\r
1195 for x in range(x1, x2+stepx, stepx):
\r
1196 if not (vline and x == x1):
\r
1198 if self._data[x][y1+offsety]["h"] == 0:
\r
1199 self._data[x][y1+offsety]["h"] = 1
\r
1200 if self._data[x][y2+stepy+offsety]["h"] == 0:
\r
1201 self._data[x][y2+stepy+offsety]["h"] = 1
\r
1202 for y in range(y1, y2+stepy, stepy):
\r
1203 if not (hline and y == y1):
\r
1205 if self._data[x1+offsetx][y]["v"] == 0:
\r
1206 self._data[x1+offsetx][y]["v"] = 1
\r
1207 if self._data[x2+stepx+offsetx][y]["v"] == 0:
\r
1208 self._data[x2+stepx+offsetx][y]["v"] = 1
\r
1210 def fillbackcolor(self, x1, y1, x2, y2, backcolor):
\r
1211 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1212 for x in range(x1, x2+stepx, stepx):
\r
1213 for y in range(y1, y2+stepy, stepy):
\r
1214 self.setbackcolor(x, y, backcolor)
\r
1216 def getstep(self, x1, y1, x2, y2):
\r
1225 return (stepx, stepy)
\r
1227 def getoffset(self, x1, y1, x2, y2):
\r
1236 return (offsetx, offsety)
\r
1238 def save(self, filename):
\r
1239 dt = self.savestring()
\r
1240 with codecs.open(filename, "w") as f:
\r
1242 self.filename = filename
\r
1244 def savestring(self):
\r
1246 data["size"] = {"x":self.width, "y":self.height}
\r
1247 data["offset"] = {"x":self.offsetx, "y":self.offsety}
\r
1248 data["sign"] = {"x":self.signx, "y":self.signy}
\r
1249 data["map"] = self.getmapstring()
\r
1253 for nk, ni in self._note.items():
\r
1254 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"]:
\r
1255 x, y = self.keytocood(nk)
\r
1256 n[self.coodtokey(self.viewx(x), self.viewy(y))] = ni
\r
1258 return yaml.safe_dump(data, allow_unicode=True,
\r
1259 default_flow_style=False, encoding='utf-8')
\r
1261 def getmapstring(self):
\r
1264 for y in range(self.height):
\r
1266 for x in range(self.width):
\r
1268 s.append(self.hwall[self._data[x][y]["h"]])
\r
1271 m.append("".join(s))
\r
1273 for x in range(self.width):
\r
1274 s.append(self.vwall[self._data[x][y]["v"]])
\r
1276 s.append(self.vwall[self._data[self.width][y]["v"]])
\r
1278 m.append("".join(s))
\r
1281 for x in range(self.width):
\r
1283 s.append(self.hwall[self._data[x][y]["h"]])
\r
1286 m.append("".join(s))
\r
1289 def load(self, filename):
\r
1290 with codecs.open(filename, "r", encoding="utf-8") as f:
\r
1292 self.loadfromstring(st)
\r
1293 self.filename = filename
\r
1295 def loadfromstring(self, st):
\r
1296 data = yaml.safe_load(st)
\r
1299 self._width = data["size"]["x"]
\r
1300 self._height = data["size"]["y"]
\r
1301 self._signx = data["sign"]["x"]
\r
1302 self._signy = data["sign"]["y"]
\r
1303 self._offsetx = data["offset"]["x"]
\r
1304 self._offsety = data["offset"]["y"]
\r
1308 for y in range(self.height):
\r
1309 for x in range(self.width):
\r
1310 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1311 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1313 for y in range(self.height):
\r
1314 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1316 for x in range(self.width):
\r
1317 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1321 for nk, ni in data["note"].items():
\r
1322 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":
\r
1323 x, y = self.keytocood(nk)
\r
1324 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni
\r
1329 class UndoManager(QtCore.QObject):
\r
1330 MAX_UNDO_COUNT = 128
\r
1331 changed = QtCore.Signal(bool, bool)
\r
1333 def __init__(self):
\r
1334 super(UndoManager, self).__init__()
\r
1338 self._undo = [None for x in range(self.MAX_UNDO_COUNT)]
\r
1340 self._undocount = 0
\r
1341 self._commited = True
\r
1342 self.changed.emit(self.canundo, self.canredo)
\r
1344 def init(self, data):
\r
1347 self._commited = True
\r
1349 def save(self, obj):
\r
1350 if self._index >= self.MAX_UNDO_COUNT:
\r
1351 self._undo = self._undo[1:]
\r
1353 self._undo.append(None)
\r
1354 self._undo[self._index] = obj
\r
1356 self._undocount = 0
\r
1357 self._commited = False
\r
1358 self.changed.emit(self.canundo, self.canredo)
\r
1362 self._undocount += 1
\r
1363 self._commited = False
\r
1364 self.changed.emit(self.canundo, self.canredo)
\r
1365 return self._undo[self._index - 1]
\r
1369 self._undocount -= 1
\r
1370 self._commited = False
\r
1371 self.changed.emit(self.canundo, self.canredo)
\r
1372 return self._undo[self._index - 1]
\r
1375 self._commited = True
\r
1378 def canundo(self):
\r
1379 return (self._index > 1)
\r
1382 def canredo(self):
\r
1383 return (self._undocount > 0)
\r
1386 def commited(self):
\r
1387 return self._commited
\r
1390 class PydunColorDialog(QtGui.QColorDialog):
\r
1391 def __init__(self, parent, config):
\r
1392 super(PydunColorDialog, self).__init__(parent)
\r
1393 for index in range(self.customCount()):
\r
1394 self.setCustomColor(index,
\r
1395 getcolorfromstring(
\r
1396 config.get(index, "#FFFFFF")).rgb())
\r
1397 self.updateconfig()
\r
1399 def updateconfig(self):
\r
1400 self._config = dict()
\r
1401 for index in range(self.customCount()):
\r
1402 self._config[index] = getcolorstring(
\r
1403 QtGui.QColor.fromRgb(self.customColor(index)))
\r
1406 super(PydunColorDialog, self).exec_()
\r
1407 self.updateconfig()
\r
1411 return self._config
\r
1414 class PydunAskSaveDialog(QtGui.QMessageBox):
\r
1415 def __init__(self, parent, filename):
\r
1416 super(PydunAskSaveDialog, self).__init__(parent)
\r
1417 self.setText(u"{filename} への変更を保存しますか?".format(filename=filename))
\r
1418 self.setStandardButtons(QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel)
\r
1419 self.setDefaultButton(QtGui.QMessageBox.Save)
\r
1420 self.button(QtGui.QMessageBox.Save).setText(u"保存する(&S)")
\r
1421 self.button(QtGui.QMessageBox.Discard).setText(u"保存しない(&N)")
\r
1422 self.button(QtGui.QMessageBox.Cancel).setText(u"キャンセル")
\r
1425 def getcolorstring(color):
\r
1426 return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())
\r
1428 def getcolorfromstring(colorstring):
\r
1429 return QtGui.QColor.fromRgb(
\r
1430 int(colorstring[1:3], 16),
\r
1431 int(colorstring[3:5], 16),
\r
1432 int(colorstring[5:7], 16))
\r
1435 return os.path.dirname(os.path.abspath(unicode(sys.argv[0], locale.getpreferredencoding())))
\r
1437 def getlatestversion():
\r
1439 rss = urllib.urlopen(projectrssurl)
\r
1440 rssstring = rss.read()
\r
1441 rsstree = xml.etree.ElementTree.fromstring(rssstring)
\r
1442 item = rsstree.find("channel/item/title")
\r
1443 ver = (item.text.split(" "))[2]
\r
1446 ver = projectversion
\r
1452 app = QtGui.QApplication(sys.argv)
\r
1453 mainWin = MainWindow()
\r
1454 app.installEventFilter(mainWin.centralWidget().mapframe)
\r
1456 sys.exit(app.exec_())
\r
1460 global configfilename
\r
1461 configfilename = os.path.join(
\r
1465 with open(configfilename, "r") as f:
\r
1466 config = yaml.safe_load(f)
\r
1470 if __name__ == '__main__':
\r