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
15 import urllib.request, urllib.parse, urllib.error
\r
16 import xml.etree.ElementTree
\r
18 from PySide import QtCore, QtGui
\r
26 projecturl = "http://osdn.jp/projects/pydun/"
\r
27 projectrssurl = "http://osdn.jp/projects/pydun/releases/rss"
\r
28 projectversion = "1.1.0"
\r
31 class MainWindow(QtGui.QMainWindow):
\r
33 def __init__(self, parent=None):
\r
38 super(MainWindow, self).__init__(parent)
\r
40 _undomanager = UndoManager()
\r
41 _mapimages = MapImages(config.get("showWallMenuString", False))
\r
43 _undomanager.changed.connect(self.updateundostate)
\r
46 if len(sys.argv) >= 2:
\r
47 self.open(sys.argv[1])
\r
49 self.mainframe = MainFrame(self)
\r
50 self.setCentralWidget(self.mainframe)
\r
52 self.statusbar = QtGui.QStatusBar(self)
\r
53 self.statusbar.showMessage("")
\r
54 self.setStatusBar(self.statusbar)
\r
55 if "windowSize" in config:
\r
58 config["windowSize"]["width"],
\r
59 config["windowSize"]["height"]))
\r
64 filemenu = self.menuBar().addMenu("ファイル(&F)")
\r
66 newact = QtGui.QAction("新規(&N)", self)
\r
67 newact.triggered.connect(self.new_triggered)
\r
68 newact.setShortcut(QtGui.QKeySequence.New)
\r
69 filemenu.addAction(newact)
\r
71 openact = QtGui.QAction("開く(&O)...", self)
\r
72 openact.triggered.connect(self.open_triggered)
\r
73 openact.setShortcut(QtGui.QKeySequence.Open)
\r
74 filemenu.addAction(openact)
\r
76 saveact = QtGui.QAction("上書き保存(&S)", self)
\r
77 saveact.triggered.connect(self.save_triggered)
\r
78 saveact.setShortcut(QtGui.QKeySequence.Save)
\r
79 filemenu.addAction(saveact)
\r
81 saveasact = QtGui.QAction("名前をつけて保存(&A)...", self)
\r
82 saveasact.triggered.connect(self.saveas_triggered)
\r
83 saveasact.setShortcut(QtGui.QKeySequence.SaveAs)
\r
84 filemenu.addAction(saveasact)
\r
86 exitact = QtGui.QAction("終了(&E)", self)
\r
87 exitact.triggered.connect(self.exit_triggered)
\r
88 exitact.setShortcut(QtGui.QKeySequence.Quit)
\r
89 filemenu.addAction(exitact)
\r
92 editmenu = self.menuBar().addMenu("編集(&E)")
\r
93 self.undoact = QtGui.QAction("元に戻す(&U)", self)
\r
94 self.undoact.triggered.connect(self.undo_triggered)
\r
95 self.undoact.setShortcut(QtGui.QKeySequence.Undo)
\r
96 editmenu.addAction(self.undoact)
\r
97 self.redoact = QtGui.QAction("やり直し(&R)", self)
\r
98 self.redoact.triggered.connect(self.redo_triggered)
\r
99 self.redoact.setShortcut(QtGui.QKeySequence.Redo)
\r
100 editmenu.addAction(self.redoact)
\r
101 editmenu.addSeparator()
\r
102 setmapsizeact = QtGui.QAction("マップのサイズ(&S)", self)
\r
103 setmapsizeact.triggered.connect(self.setmapsize_triggered)
\r
104 editmenu.addAction(setmapsizeact)
\r
105 setorigineact = QtGui.QAction("座標設定(&O)", self)
\r
106 setorigineact.triggered.connect(self.setorigine_triggered)
\r
107 editmenu.addAction(setorigineact)
\r
108 wallmenustringact = QtGui.QAction("壁メニューに文字を表示する(&W)", self)
\r
109 wallmenustringact.setCheckable(True)
\r
110 wallmenustringact.setChecked(config.get("showWallMenuString", False))
\r
111 wallmenustringact.triggered.connect(self.togglewallmenustring_triggered)
\r
112 editmenu.addAction(wallmenustringact)
\r
115 helpmenu = self.menuBar().addMenu("ヘルプ(&H)")
\r
116 tutorialact = QtGui.QAction("ヘルプの表示(&H)", self)
\r
117 tutorialact.triggered.connect(self.tutorial_triggered)
\r
118 tutorialact.setShortcut(QtGui.QKeySequence.HelpContents)
\r
119 helpmenu.addAction(tutorialact)
\r
120 projectact = QtGui.QAction("プロジェクトのWebサイト(&W)", self)
\r
121 projectact.triggered.connect(self.project_triggered)
\r
122 helpmenu.addAction(projectact)
\r
123 aboutact = QtGui.QAction("Pydunについて(&A)...", self)
\r
124 aboutact.triggered.connect(self.about_triggered)
\r
125 helpmenu.addAction(aboutact)
\r
127 @QtCore.Slot(bool, bool)
\r
128 def updateundostate(self, canundo, canredo):
\r
130 self.undoact.setEnabled(True)
\r
132 self.undoact.setDisabled(True)
\r
134 self.redoact.setEnabled(True)
\r
136 self.redoact.setDisabled(True)
\r
138 def setTitle(self, filename):
\r
139 s = self.getfilename(filename) + " - Pydun"
\r
140 self.setWindowTitle(s)
\r
142 def getfilename(self, filename):
\r
143 if filename == None:
\r
146 s = os.path.splitext(os.path.basename(filename))[0]
\r
150 def new_triggered(self):
\r
151 if self.confirmdiscarding():
\r
158 _mapengine = MapEngine(20, 20, 1, -1, 0, +19)
\r
159 _undomanager.init(_mapengine.savestring())
\r
160 self.setTitle(None)
\r
162 self.mainframe.mapframe.repaint()
\r
166 def confirmdiscarding(self):
\r
167 if not _undomanager.commited:
\r
168 dlg = PydunAskSaveDialog(self, self.getfilename(_mapengine.filename))
\r
170 if ret == QtGui.QMessageBox.Cancel:
\r
172 elif ret == QtGui.QMessageBox.Save:
\r
173 saved = self.save_triggered()
\r
179 def open_triggered(self):
\r
180 if self.confirmdiscarding():
\r
183 d = os.path.dirname(_mapengine.filename)
\r
186 filename = QtGui.QFileDialog.getOpenFileName(
\r
188 filter="*.pydun;;*.*", selectedFilter="*.pydun")
\r
189 if filename[0] != "":
\r
190 self.open(filename[0])
\r
192 def open(self, filename):
\r
193 _mapengine.load(filename)
\r
194 _undomanager.init(_mapengine.savestring())
\r
195 self.setTitle(_mapengine.filename)
\r
197 self.mainframe.mapframe.repaint()
\r
202 def save_triggered(self):
\r
203 if _mapengine.filename:
\r
204 self.save(_mapengine.filename)
\r
207 saved = self.saveas_triggered()
\r
211 def saveas_triggered(self):
\r
214 d = os.path.dirname(_mapengine.filename)
\r
217 filename = QtGui.QFileDialog.getSaveFileName(
\r
219 filter="*.pydun;;*.*", selectedFilter="*.pydun")
\r
220 if filename[0] != "":
\r
221 self.save(filename[0])
\r
226 def save(self, filename):
\r
227 _mapengine.save(filename)
\r
228 _undomanager.commit()
\r
229 self.setTitle(_mapengine.filename)
\r
232 def exit_triggered(self):
\r
235 def closeEvent(self, event):
\r
243 global configfilename
\r
244 if self.confirmdiscarding():
\r
245 config["windowSize"] = dict()
\r
246 config["windowSize"]["width"] = self.size().width()
\r
247 config["windowSize"]["height"] = self.size().height()
\r
248 with open(configfilename, "w") as f:
\r
249 yaml.safe_dump(config, f, default_flow_style=False)
\r
255 def undo_triggered(self):
\r
257 _mapengine.loadfromstring(_undomanager.undo())
\r
258 self.mainframe.mapframe.repaint()
\r
261 def redo_triggered(self):
\r
263 _mapengine.loadfromstring(_undomanager.redo())
\r
264 self.mainframe.mapframe.repaint()
\r
267 def setorigine_triggered(self):
\r
269 if self.mainframe.mapframe.setoriginemode:
\r
270 QtGui.QMessageBox.information(
\r
271 self, title, "座標設定を中止します。", QtGui.QMessageBox.Ok)
\r
272 self.mainframe.mapframe.setoriginemode = False
\r
274 if QtGui.QMessageBox.Ok == QtGui.QMessageBox.information(
\r
275 self, title, "基準にする地点をクリックしてください。",
\r
276 (QtGui.QMessageBox.Ok| QtGui.QMessageBox.Cancel)):
\r
277 self.mainframe.mapframe.setoriginemode = True
\r
280 def setmapsize_triggered(self):
\r
281 dlg = SetSizeDialog(self)
\r
282 dlg.setoriginalsize(_mapengine.width, _mapengine.height)
\r
284 if dlg.result() == QtGui.QDialog.Accepted:
\r
285 top, bottom, left, right = dlg.getsize()
\r
286 _mapengine.changesize(top, bottom, left, right)
\r
287 _undomanager.save(_mapengine.savestring())
\r
288 self.mainframe.mapframe.repaint()
\r
291 def togglewallmenustring_triggered(self):
\r
293 config["showWallMenuString"] = not config.get("showWallMenuString", False)
\r
294 QtGui.QMessageBox.information(
\r
295 self, "壁メニューに文字を表示する", "表示の切替は再起動後に有効になります。",
\r
296 (QtGui.QMessageBox.Ok))
\r
299 def tutorial_triggered(self):
\r
300 url = basedir() + "/help/index.html"
\r
301 webbrowser.open_new_tab(url)
\r
304 def project_triggered(self):
\r
305 webbrowser.open_new_tab(projecturl)
\r
308 def about_triggered(self):
\r
309 QtGui.QMessageBox.about(self, "Pydun",
\r
310 "<h1>Pydun.py "+ projectversion + "</h1>"
\r
311 "<p>Copyright (c) 2013-2015 WATAHIKI Hiroyuki</p>"
\r
312 "<p>url: <a href='" + projecturl + "'>" + projecturl + "</a></p>"
\r
313 "<p>e-mail: hrwatahiki at gmail.com</p>"
\r
314 "<p>twitter: <a href='https://twitter.com/hrwatahiki'>@hrwatahiki</a></p>"
\r
315 "<p>blog: <a href='http://hrwatahiki.blogspot.jp/'>作業記録</a></p>"
\r
316 "<p>このソフトウェアはMITライセンスです。</p>"
\r
317 "<p>このソフトウェアは以下のソフトウェアを使用しています。: "
\r
318 "Python, PySide, PyYAML "
\r
319 "これらの作成者に深く感謝いたします。</p>"
\r
320 "<p>詳細はLICENCE.txtを参照してください。</p>")
\r
323 class MainFrame(QtGui.QFrame):
\r
324 create_wall_menu_triggered_signal = QtCore.Signal(int, int, str, int)
\r
326 def __init__(self, parent=None):
\r
327 super(MainFrame, self).__init__(parent)
\r
329 self.mapframe = MapFrame(self)
\r
330 scrollarea = QtGui.QScrollArea(self)
\r
331 scrollarea.setWidget(self.mapframe)
\r
333 self.detail = QtGui.QLabel(self)
\r
334 self.detail.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
335 self.detail.setText("")
\r
336 self.detail.setMaximumHeight(100)
\r
337 self.detail.setMinimumHeight(100)
\r
339 self.boxdrawbutton = QtGui.QRadioButton(self)
\r
340 self.boxdrawbutton.setText("ボックス形式で壁を描画(&B)")
\r
341 self.boxdrawbutton.setChecked(True)
\r
342 self.boxdrawbutton.setSizePolicy(
\r
343 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
345 self.growdrawbutton = QtGui.QRadioButton(self)
\r
346 self.growdrawbutton.setText("足跡形式で壁を描画(&G)")
\r
347 self.growdrawbutton.setChecked(False)
\r
348 self.growdrawbutton.setSizePolicy(
\r
349 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
351 self.backcolorbutton = QtGui.QRadioButton(self)
\r
352 self.backcolorbutton.setText("背景色(&C)")
\r
353 self.backcolorbutton.setChecked(False)
\r
354 self.backcolorbutton.setSizePolicy(
\r
355 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
357 self.setbackcolorbutton = QtGui.QPushButton(self)
\r
358 self.setbackcolorbutton.setText("背景色を設定(&S)...")
\r
359 self.setbackcolorbutton.setSizePolicy(
\r
360 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
362 self.backcolorbox = ColorBox(self)
\r
363 self.backcolorbox.setMinimumSize(30, 30)
\r
364 self.backcolorbox.setSizePolicy(
\r
365 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
367 latestversion = getlatestversion()
\r
368 if latestversion != projectversion:
\r
369 self.update = QtGui.QLabel(self)
\r
370 self.update.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
371 self.update.setText("<a href='{url}'>最新のPydun({ver})がダウンロードできます。</a>".format(url=projecturl, ver=latestversion))
\r
372 self.update.setOpenExternalLinks(True)
\r
374 layout = QtGui.QGridLayout(self)
\r
375 layout.addWidget(scrollarea, 0, 0, 1, 3)
\r
376 layout.addWidget(self.detail, 1, 0, 4, 1)
\r
377 layout.addWidget(self.boxdrawbutton, 1, 1, 1, 2)
\r
378 layout.addWidget(self.growdrawbutton, 2, 1, 1, 2)
\r
379 layout.addWidget(self.backcolorbutton, 3, 1, 1, 2)
\r
380 layout.addWidget(self.setbackcolorbutton, 4, 1, 1, 1)
\r
381 layout.addWidget(self.backcolorbox, 4, 2, 1, 1)
\r
382 if latestversion != projectversion:
\r
383 layout.addWidget(self.update, 5, 0, 1, 3)
\r
385 self.setLayout(layout)
\r
387 self.h_wall_menu = self.create_wall_menu("h")
\r
388 self.v_wall_menu = self.create_wall_menu("v")
\r
390 self.mapframe.mouse_moved.connect(self.mouse_moved)
\r
391 self.mapframe.mouse_released.connect(self.mouse_released)
\r
392 self.mapframe.mouse_drag_released.connect(self.mouse_drag_released)
\r
393 self.create_wall_menu_triggered_signal.connect(
\r
394 self.create_wall_menu_triggered)
\r
395 self.setbackcolorbutton.clicked.connect(
\r
396 self.setbackcolorbutton_clicked)
\r
398 def create_wall_menu(self, direction):
\r
399 menu = QtGui.QMenu(self)
\r
400 for idx, img in enumerate(_mapimages.wall_icons):
\r
401 act = QtGui.QAction(_mapimages.wall_texts[idx][direction], self)
\r
402 act.setIcon(img[direction])
\r
406 self.create_wall_menu_triggered_signal.emit(menu.x, menu.y, direction, idx)
\r
409 act.triggered.connect(triggerd(idx))
\r
410 menu.addAction(act)
\r
413 @QtCore.Slot(int, int, int)
\r
414 def mouse_moved(self, x=0, y=0, b=QtCore.Qt.MouseButton.NoButton):
\r
415 cood = "({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))
\r
416 self.detail.setText(cood + _mapengine.getdetail(x, y))
\r
417 self.mapframe.repaint()
\r
419 @QtCore.Slot(int, int, int, int, int)
\r
420 def mouse_drag_released(self, x1, y1, x2, y2, eraseonly):
\r
421 if self.boxdrawbutton.isChecked():
\r
422 _mapengine.growwall(x1, y1, x2, y2, eraseonly, True)
\r
423 elif self.growdrawbutton.isChecked():
\r
424 _mapengine.growwall(x1, y1, x2, y2, eraseonly, False)
\r
425 elif self.backcolorbutton.isChecked():
\r
429 backcolor = getcolorstring(self.backcolorbox.color)
\r
430 _mapengine.fillbackcolor(x1, y1, x2, y2, backcolor)
\r
431 _undomanager.save(_mapengine.savestring())
\r
432 self.mapframe.repaint()
\r
434 @QtCore.Slot(int, int, str)
\r
435 def mouse_released(self, x1, y1, direction):
\r
437 if self.mapframe.setoriginemode:
\r
438 dlg = SetOrigineDialog(self)
\r
439 dlg.setcurrent(_mapengine.viewx(x1), _mapengine.viewy(y1))
\r
440 dlg.exec_() #showでは処理がとまらない。
\r
441 if dlg.result() == QtGui.QDialog.Accepted:
\r
442 _mapengine.setoffset(
\r
443 dlg.originex - _mapengine.viewx(x1) + _mapengine.offsetx,
\r
444 dlg.originey - _mapengine.viewy(y1) + _mapengine.offsety
\r
446 _undomanager.save(_mapengine.savestring())
\r
447 self.mapframe.setoriginemode = False
\r
450 if direction == "c":
\r
451 dlg = DetailDialog(self)
\r
452 dlg.setvalue(_mapengine.viewx(x1), _mapengine.viewy(y1),
\r
453 _mapengine.getmark(x1, y1), _mapengine.getdetail(x1, y1),
\r
454 getcolorfromstring(_mapengine.getforecolor(x1, y1)))
\r
455 dlg.exec_() #showでは処理がとまらない。
\r
456 if dlg.result() == QtGui.QDialog.Accepted:
\r
457 forecolor = getcolorstring(dlg.forecolorbox.color)
\r
458 _mapengine.setmark(x1, y1, dlg.marktext.text())
\r
459 _mapengine.setdetail(x1, y1, dlg.detailtext.toPlainText())
\r
460 _mapengine.setforecolor(x1, y1, forecolor)
\r
461 _undomanager.save(_mapengine.savestring())
\r
462 self.mapframe.repaint()
\r
466 if direction == "h":
\r
467 menu = self.h_wall_menu
\r
468 elif direction == "v":
\r
469 menu = self.v_wall_menu
\r
472 menu.popup(QtGui.QCursor.pos())
\r
474 @QtCore.Slot(int, int, str, int)
\r
475 def create_wall_menu_triggered(self, x1, y1, direction, wall):
\r
476 _mapengine.setdata(x1, y1, direction, wall)
\r
477 _undomanager.save(_mapengine.savestring())
\r
478 self.mapframe.repaint()
\r
481 def setbackcolorbutton_clicked(self):
\r
483 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
484 dlg.setCurrentColor(self.backcolorbox.color)
\r
486 config["customColor"] = dlg.config
\r
487 if dlg.result() == QtGui.QDialog.Accepted:
\r
488 self.backcolorbox.color = dlg.currentColor()
\r
489 self.backcolorbutton.setChecked(True)
\r
492 class MapFrame(QtGui.QFrame):
\r
493 mouse_moved = QtCore.Signal(int, int, int)
\r
494 mouse_released = QtCore.Signal(int, int, str)
\r
495 mouse_drag_released = QtCore.Signal(int, int, int, int, int)
\r
499 def __init__(self, parent=None):
\r
500 super(MapFrame, self).__init__(parent)
\r
501 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
510 self._dragging = False
\r
511 self.setoriginemode = False
\r
512 self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
514 _mapimages.width * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
515 _mapimages.height * (_mapengine.height) + _mapimages.heightoffset * 2
\r
518 def paintEvent(self, event):
\r
519 painter = QtGui.QPainter(self)
\r
520 painter.fillRect(0, 0, self.width(), self.height(), QtGui.QColor(255, 255, 255))
\r
521 w = _mapimages.width - 1
\r
522 v = _mapimages.height - 1
\r
523 ho = _mapimages.heightoffset
\r
524 wo = _mapimages.widthoffset
\r
528 w * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
529 v * (_mapengine.height) + _mapimages.heightoffset * 2
\r
533 for x in range(_mapengine.width):
\r
535 for y in range(_mapengine.height):
\r
537 backcolor = _mapengine.getbackcolor(x, y)
\r
539 painter.fillRect(wo + xx, ho + yy, w, v,
\r
540 getcolorfromstring(backcolor))
\r
543 for x in range(_mapengine.width + 1):
\r
545 for y in range(_mapengine.height + 1):
\r
547 if x != _mapengine.width:
\r
548 painter.drawImage(wo + xx, yy,
\r
549 _mapimages.wall(0, "h"))
\r
550 if y != _mapengine.height:
\r
551 painter.drawImage(xx, ho + yy,
\r
552 _mapimages.wall(0, "v"))
\r
555 for x in range(_mapengine.width + 1):
\r
557 for y in range(_mapengine.height + 1):
\r
559 if x != _mapengine.width and _mapengine.getdata(x, y, "h") != 0:
\r
560 painter.drawImage(wo + xx, yy,
\r
561 _mapimages.wall(_mapengine.getdata(x, y, "h"), "h"))
\r
562 if y != _mapengine.height and _mapengine.getdata(x, y, "v") != 0:
\r
563 painter.drawImage(xx, ho + yy,
\r
564 _mapimages.wall(_mapengine.getdata(x, y, "v"), "v"))
\r
565 mark = _mapengine.getmark(x, y)
\r
567 painter.setPen(getcolorfromstring(_mapengine.getforecolor(x, y)))
\r
568 painter.drawText(wo + xx + 2, ho + yy + 2, w - 2, v - 2,
\r
569 QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter,
\r
572 #座標設定中はdrawing box を表示しない。
\r
573 if self.setoriginemode:
\r
577 if self._pressedbutton != QtCore.Qt.MouseButton.NoButton:
\r
578 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
579 if self._x1 == self._x2 and self._y1 == self._y2:
\r
580 painter.setPen(QtGui.QColor(255, 0, 0))
\r
581 elif self._x1 == self._x2 or self._y1 == self._y2:
\r
582 painter.setPen(QtGui.QColor(0, 255, 0))
\r
584 painter.setPen(QtGui.QColor(255, 0, 0))
\r
585 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
586 painter.setPen(QtGui.QColor(0, 0, 255))
\r
587 painter.drawRect(self._px1, self._py1,
\r
588 self._px2 - self._px1, self._py2 - self._py1)
\r
590 def eventFilter(self, obj, event):
\r
592 return ((event.pos().x() - _mapimages.widthoffset) // (_mapimages.width - 1))
\r
595 return ((event.pos().y() - _mapimages.heightoffset) // (_mapimages.height - 1))
\r
600 if et == QtCore.QEvent.MouseButtonPress:
\r
603 self._pos1 = event.pos()
\r
604 self._px1 = event.pos().x()
\r
605 self._py1 = event.pos().y()
\r
608 self._px2 = event.pos().x()
\r
609 self._py2 = event.pos().y()
\r
610 self._pressedbutton = event.buttons()
\r
611 self._dragging = False
\r
614 elif et == QtCore.QEvent.MouseMove:
\r
617 self._px2 = event.pos().x()
\r
618 self._py2 = event.pos().y()
\r
619 if (self._pressedbutton != QtCore.Qt.MouseButton.NoButton and
\r
620 (event.pos() - self._pos1).manhattanLength() >=
\r
621 QtGui.QApplication.startDragDistance()):
\r
622 self._dragging = True
\r
623 self.mouse_moved.emit(self._x2, self._y2, event.buttons())
\r
626 elif et == QtCore.QEvent.MouseButtonRelease:
\r
628 release_emit = False
\r
631 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
633 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
636 release_emit = True
\r
637 if self.setoriginemode:
\r
638 release_emit = True
\r
640 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
641 self._dragging = False
\r
643 self.mouse_drag_released.emit(
\r
644 self._x1, self._y1, self._x2, self._y2, eraseonly)
\r
646 rpx = self._px2 - self._x2 * (_mapimages.width - 1) - _mapimages.widthoffset
\r
647 rpy = self._py2 - self._y2 * (_mapimages.height - 1) - _mapimages.heightoffset
\r
648 rdx = rpx - (_mapimages.width - 1) // 2
\r
649 rdy = rpy - (_mapimages.height - 1) // 2
\r
650 if rpx <= _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
654 elif rpx >= _mapimages.width - _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
658 elif rpy <= _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
662 elif rpy >= _mapimages.height - _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
670 self.mouse_released.emit(rx, ry, d)
\r
676 # pass the event on to the parent class
\r
680 class ColorBox(QtGui.QFrame):
\r
681 def __init__(self, parent=None):
\r
682 super(ColorBox, self).__init__(parent)
\r
683 self.color = QtGui.QColor(255, 255, 255)
\r
684 self.bordercolor = QtGui.QColor(0, 0, 0)
\r
686 def paintEvent(self, event):
\r
687 painter = QtGui.QPainter(self)
\r
688 painter.fillRect(0, 0, self.width(), self.height(), self.color)
\r
689 painter.setPen(self.bordercolor)
\r
690 painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
\r
693 class DetailDialog(QtGui.QDialog):
\r
694 def __init__(self, parent=None):
\r
695 super(DetailDialog, self).__init__(parent)
\r
697 marklabel = QtGui.QLabel(self)
\r
698 marklabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
699 marklabel.setText("マーク(&M)")
\r
700 marklabel.setSizePolicy(
\r
701 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
703 self.marktext = QtGui.QLineEdit(self)
\r
704 self.marktext.setMaxLength(1)
\r
705 self.marktext.setText("")
\r
706 self.marktext.setMinimumWidth(20)
\r
707 self.marktext.setSizePolicy(
\r
708 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
709 marklabel.setBuddy(self.marktext)
\r
711 self.forecolorbutton = QtGui.QPushButton(self)
\r
712 self.forecolorbutton.setText("文字色(&C)...")
\r
713 self.forecolorbutton.clicked.connect(self.forecolorbutton_clicked)
\r
715 self.forecolorbox = ColorBox(self)
\r
716 self.forecolorbox.setMinimumSize(30, 30)
\r
717 self.forecolorbox.setSizePolicy(
\r
718 QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
\r
720 detaillabel = QtGui.QLabel(self)
\r
721 detaillabel.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight)
\r
722 detaillabel.setText("詳細(&D)")
\r
724 self.detailtext = QtGui.QTextEdit(self)
\r
725 self.detailtext.setText("")
\r
726 detaillabel.setBuddy(self.detailtext)
\r
728 self.buttonbox = QtGui.QDialogButtonBox(
\r
729 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
730 self.buttonbox.accepted.connect(self.accept)
\r
731 self.buttonbox.rejected.connect(self.reject)
\r
732 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")
\r
733 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")
\r
735 layout = QtGui.QGridLayout()
\r
736 layout.addWidget(marklabel, 0, 0, 1, 1)
\r
737 layout.addWidget(self.marktext, 0, 1, 1, 1)
\r
738 layout.addWidget(self.forecolorbutton, 0, 2, 1, 1)
\r
739 layout.addWidget(self.forecolorbox, 0, 3, 1, 1)
\r
740 layout.addWidget(detaillabel, 1, 0, 1, 1)
\r
741 layout.addWidget(self.detailtext, 1, 1, 1, 3)
\r
742 layout.addWidget(self.buttonbox, 2, 0, 1, 4)
\r
743 self.setLayout(layout)
\r
744 self.setModal(True)
\r
746 def setvalue(self, x, y, mark, detail, color):
\r
747 self.setWindowTitle("({x}, {y})".format(x=x, y=y))
\r
748 self.marktext.setText(mark)
\r
749 self.detailtext.setText(detail)
\r
750 self.forecolorbox.color = color
\r
752 def forecolorbutton_clicked(self):
\r
754 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
755 dlg.setCurrentColor(self.forecolorbox.color)
\r
757 config["customColor"] = dlg.config
\r
758 if dlg.result() == QtGui.QDialog.Accepted:
\r
759 self.forecolorbox.color = dlg.currentColor()
\r
762 class SetOrigineDialog(QtGui.QDialog):
\r
763 def __init__(self, parent=None):
\r
764 super(SetOrigineDialog, self).__init__(parent)
\r
765 self.setWindowTitle("座標設定")
\r
767 promptlabel = QtGui.QLabel(self)
\r
768 promptlabel.setAlignment(
\r
769 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
770 promptlabel.setText("この地点の座標を入力してください。")
\r
772 self.currentlabel = QtGui.QLabel(self)
\r
773 self.currentlabel.setAlignment(
\r
774 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
\r
775 self.currentlabel.setText("")
\r
777 xlabel = QtGui.QLabel(self)
\r
778 xlabel.setAlignment(
\r
779 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
780 xlabel.setText("&X")
\r
782 self.xbox = QtGui.QSpinBox(self)
\r
783 self.xbox.setRange(-999, +999)
\r
784 self.xbox.setSingleStep(1)
\r
785 self.xbox.setValue(0)
\r
786 xlabel.setBuddy(self.xbox)
\r
788 ylabel = QtGui.QLabel(self)
\r
789 ylabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
790 ylabel.setText("&Y")
\r
792 self.ybox = QtGui.QSpinBox(self)
\r
793 self.ybox.setRange(-999, +999)
\r
794 self.ybox.setSingleStep(1)
\r
795 self.ybox.setValue(0)
\r
796 ylabel.setBuddy(self.ybox)
\r
798 self.buttonbox = QtGui.QDialogButtonBox(
\r
799 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
800 self.buttonbox.accepted.connect(self.accept)
\r
801 self.buttonbox.rejected.connect(self.reject)
\r
802 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")
\r
803 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")
\r
805 layout = QtGui.QGridLayout()
\r
806 layout.addWidget(promptlabel, 0, 0, 1, 4)
\r
807 layout.addWidget(self.currentlabel, 1, 0, 1, 4)
\r
808 layout.addWidget(xlabel, 2, 0, 1, 1)
\r
809 layout.addWidget(self.xbox, 2, 1, 1, 1)
\r
810 layout.addWidget(ylabel, 2, 2, 1, 1)
\r
811 layout.addWidget(self.ybox, 2, 3, 1, 1)
\r
812 layout.addWidget(self.buttonbox, 3, 0, 1, 4)
\r
813 self.setLayout(layout)
\r
814 self.setModal(True)
\r
816 def setcurrent(self, x, y):
\r
817 self.xbox.setValue(x)
\r
818 self.ybox.setValue(y)
\r
819 self.currentlabel.setText("現在の座標 ({x}, {y})".format(x=x, y=y))
\r
822 def originex(self):
\r
823 return self.xbox.value()
\r
826 def originey(self):
\r
827 return self.ybox.value()
\r
830 class SetSizeDialog(QtGui.QDialog):
\r
831 def __init__(self, parent=None):
\r
832 super(SetSizeDialog, self).__init__(parent)
\r
833 self.setWindowTitle("マップのサイズ")
\r
835 self.topbutton = QtGui.QRadioButton(self)
\r
836 self.topbutton.setText("上(&T)")
\r
837 self.topbutton.clicked.connect(self.updatewidgets)
\r
839 self.topsize = QtGui.QSpinBox(self)
\r
840 self.topsize.setSingleStep(1)
\r
841 self.topsize.setValue(0)
\r
842 self.topsize.valueChanged.connect(self.updatewidgets)
\r
844 self.bottombutton = QtGui.QRadioButton(self)
\r
845 self.bottombutton.setText("下(&B)")
\r
846 self.bottombutton.clicked.connect(self.updatewidgets)
\r
848 self.bottomsize = QtGui.QSpinBox(self)
\r
849 self.bottomsize.setSingleStep(1)
\r
850 self.bottomsize.setValue(0)
\r
851 self.bottomsize.valueChanged.connect(self.updatewidgets)
\r
853 self.leftbutton = QtGui.QRadioButton(self)
\r
854 self.leftbutton.setText("左(&L)")
\r
855 self.leftbutton.clicked.connect(self.updatewidgets)
\r
857 self.leftsize = QtGui.QSpinBox(self)
\r
858 self.leftsize.setSingleStep(1)
\r
859 self.leftsize.setValue(0)
\r
860 self.leftsize.valueChanged.connect(self.updatewidgets)
\r
862 self.rightbutton = QtGui.QRadioButton(self)
\r
863 self.rightbutton.setText("右(&R)")
\r
864 self.rightbutton.clicked.connect(self.updatewidgets)
\r
866 self.rightsize = QtGui.QSpinBox(self)
\r
867 self.rightsize.setSingleStep(1)
\r
868 self.rightsize.setValue(0)
\r
869 self.rightsize.valueChanged.connect(self.updatewidgets)
\r
871 self.sizelabel = QtGui.QLabel(self)
\r
872 self.sizelabel .setAlignment(
\r
873 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
874 self.sizelabel.setText("この地点の座標を入力してください。")
\r
876 self.buttonbox = QtGui.QDialogButtonBox(
\r
877 QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
\r
878 self.buttonbox.accepted.connect(self.accept)
\r
879 self.buttonbox.rejected.connect(self.reject)
\r
880 self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")
\r
881 self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")
\r
883 verticalgroup = QtGui.QButtonGroup(self)
\r
884 verticalgroup.addButton(self.topbutton)
\r
885 verticalgroup.addButton(self.bottombutton)
\r
887 holizontalgroup = QtGui.QButtonGroup(self)
\r
888 holizontalgroup.addButton(self.leftbutton)
\r
889 holizontalgroup.addButton(self.rightbutton)
\r
891 self.topbutton.setChecked(True)
\r
892 self.bottombutton.setChecked(False)
\r
893 self.leftbutton.setChecked(True)
\r
894 self.rightbutton.setChecked(False)
\r
896 layout = QtGui.QGridLayout(self)
\r
897 layout.addWidget(self.topbutton, 0, 2, 1, 1)
\r
898 layout.addWidget(self.topsize, 0, 3, 1, 1)
\r
899 layout.addWidget(self.leftbutton, 1, 0, 1, 1)
\r
900 layout.addWidget(self.leftsize, 1, 1, 1, 1)
\r
901 layout.addWidget(self.sizelabel, 1, 2, 1, 2)
\r
902 layout.addWidget(self.rightbutton, 1, 4, 1, 1)
\r
903 layout.addWidget(self.rightsize, 1, 5, 1, 1)
\r
904 layout.addWidget(self.bottombutton, 2, 2, 1, 1)
\r
905 layout.addWidget(self.bottomsize, 2, 3, 1, 1)
\r
906 layout.addWidget(self.buttonbox, 3, 0, 1, 6)
\r
907 self.setLayout(layout)
\r
908 self.setModal(True)
\r
910 def setoriginalsize(self, width, height):
\r
911 self._width = width
\r
912 self._height = height
\r
913 self.topsize.setRange(-height+1, +100)
\r
914 self.bottomsize.setRange(-height+1, +100)
\r
915 self.leftsize.setRange(-width+1, +100)
\r
916 self.rightsize.setRange(-width+1, +100)
\r
917 self.updatewidgets()
\r
919 def updatewidgets(self):
\r
923 if self.topbutton.isChecked():
\r
924 dh = self.topsize.value()
\r
925 self.topsize.setEnabled(True)
\r
926 self.bottomsize.setDisabled(True)
\r
927 elif self.bottombutton.isChecked():
\r
928 dh = self.bottomsize.value()
\r
929 self.topsize.setDisabled(True)
\r
930 self.bottomsize.setEnabled(True)
\r
931 if self.leftbutton.isChecked():
\r
932 dw = self.leftsize.value()
\r
933 self.leftsize.setEnabled(True)
\r
934 self.rightsize.setDisabled(True)
\r
935 elif self.rightbutton.isChecked():
\r
936 dw = self.rightsize.value()
\r
937 self.leftsize.setDisabled(True)
\r
938 self.rightsize.setEnabled(True)
\r
940 self.sizelabel.setText(
\r
941 "変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(
\r
942 w1=self._width, h1=self._height,
\r
943 w2=self._width+dw, h2=self._height+dh))
\r
950 if self.topbutton.isChecked():
\r
951 top = self.topsize.value()
\r
952 elif self.bottombutton.isChecked():
\r
953 bottom = self.bottomsize.value()
\r
954 if self.leftbutton.isChecked():
\r
955 left = self.leftsize.value()
\r
956 elif self.rightbutton.isChecked():
\r
957 right = self.rightsize.value()
\r
958 return (top, bottom, left, right)
\r
961 class MapImages(object):
\r
962 def __init__(self, show_wall_menu_string):
\r
963 if show_wall_menu_string:
\r
964 vtext = ["なし", "壁", "扉", "扉(→)", "扉(←)", "一通(→)", "一通(←)", "隠", "隠(→)", "隠(←)",]
\r
965 htext = ["なし", "壁", "扉", "扉(↓)", "扉(↑)", "一通(↓)", "一通(↑)", "隠", "隠(↓)", "隠(↑)",]
\r
967 vtext = ["", "", "", "", "", "", "", "", "", "",]
\r
968 htext = ["", "", "", "", "", "", "", "", "", "",]
\r
969 self.wall_images = list()
\r
970 self.wall_icons = list()
\r
971 self.wall_texts = list()
\r
972 for index in range(10):
\r
973 self.wall_images.append(dict())
\r
974 self.wall_icons.append(dict())
\r
975 self.wall_texts.append(dict())
\r
976 for direction in ["v", "h"]:
\r
977 filename = os.path.join(
\r
980 "wall_{direction}_{index:02}.png".format(
\r
981 direction=direction, index=index))
\r
982 self.wall_images[index][direction] = QtGui.QImage()
\r
983 self.wall_images[index][direction].load(filename)
\r
984 self.wall_icons[index][direction] = QtGui.QIcon(filename)
\r
985 self.wall_texts[index]["v"] = vtext[index]
\r
986 self.wall_texts[index]["h"] = htext[index]
\r
990 return self.wall_images[0]["h"].width()
\r
994 return self.wall_images[0]["v"].height()
\r
997 def widthoffset(self):
\r
998 return self.wall_images[0]["v"].width()//2
\r
1001 def heightoffset(self):
\r
1002 return self.wall_images[0]["h"].height()//2
\r
1004 def wall(self, index, direction):
\r
1005 return self.wall_images[index][direction]
\r
1008 class MapEngine(object):
\r
1009 hwall = " -#WMwmHVA"
\r
1010 vwall = " |#PCpc=DG"
\r
1012 def __init__(self, width, height, signx, signy, offsetx, offsety):
\r
1013 self._width = width
\r
1014 self._height = height
\r
1015 self._signx = signx
\r
1016 self._signy = signy
\r
1017 self._offsetx = offsetx
\r
1018 self._offsety = offsety
\r
1019 self.filename = None
\r
1023 self._note = dict()
\r
1025 def initdata(self):
\r
1026 width = self.width + 1
\r
1027 height = self.height + 1
\r
1028 self._data = self.initialdata(width, height)
\r
1030 def initialdata(self, width, height):
\r
1032 for x in range(width):
\r
1034 for y in range(height):
\r
1035 dt[x].append(dict())
\r
1036 for d in ["h", "v"]:
\r
1040 def inityaml(self):
\r
1041 #yaml !python/Unicode出力抑止おまじない
\r
1042 def represent_unicode(dumper, data):
\r
1043 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
\r
1044 def construct_unicode(loader, node):
\r
1045 return str(loader.construct_scalar(node))
\r
1046 yaml.add_representer(str, represent_unicode)
\r
1047 yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)
\r
1049 def getdata(self, x, y, direction):
\r
1050 return self._data[x][y][direction]
\r
1052 def setdata(self, x, y, direction, value):
\r
1053 self._data[x][y][direction] = value
\r
1057 return self._width
\r
1061 return self._height
\r
1065 return self._signx
\r
1069 return self._signy
\r
1072 def offsetx(self):
\r
1073 return self._offsetx
\r
1076 def offsety(self):
\r
1077 return self._offsety
\r
1079 def setoffset(self, x, y):
\r
1083 def getmark(self, x, y):
\r
1084 return self.unescape(self.getnote(x, y)["mark"])
\r
1086 def getdetail(self, x, y):
\r
1087 return self.unescape(self.getnote(x, y)["detail"])
\r
1089 def getforecolor(self, x, y):
\r
1090 return self.getnote(x, y)["forecolor"]
\r
1092 def getbackcolor(self, x, y):
\r
1093 return self.getnote(x, y)["backcolor"]
\r
1095 def getnote(self, x, y):
\r
1096 return self._note.get(
\r
1097 self.coodtokey(x, y), {"mark":"", "detail":"", "forecolor":"#000000", "backcolor":""})
\r
1099 def coodtokey(self, x, y):
\r
1100 return "{x:+05d}_{y:+05d}".format(x=int(x), y=int(y))
\r
1102 def keytocood(self, key):
\r
1103 return list(map(int, key.split("_")))
\r
1105 def setmark(self, x, y, mark):
\r
1106 note = self.getnote(x, y)
\r
1107 note["mark"] = self.escape(mark)
\r
1108 self.setnote(x, y, note)
\r
1110 def setdetail(self, x, y, detail):
\r
1111 note = self.getnote(x, y)
\r
1112 note["detail"] = self.escape(detail)
\r
1113 self.setnote(x, y, note)
\r
1115 def setforecolor(self, x, y, color):
\r
1116 note = self.getnote(x, y)
\r
1117 note["forecolor"] = color
\r
1118 self.setnote(x, y, note)
\r
1120 def setbackcolor(self, x, y, color):
\r
1121 note = self.getnote(x, y)
\r
1122 note["backcolor"] = color
\r
1123 self.setnote(x, y, note)
\r
1125 def setnote(self, x, y, note):
\r
1126 self._note[self.coodtokey(x, y)] = note
\r
1128 def escape(self, s):
\r
1129 return s.replace("\\", "\\\\").replace("\n", r"\n")
\r
1131 def unescape(self, s):
\r
1132 return s.replace(r"\n", "\n").replace("\\\\", "\\")
\r
1134 def viewx(self, x):
\r
1135 return x * self.signx + self.offsetx
\r
1137 def viewy(self, y):
\r
1138 return y * self.signy + self.offsety
\r
1140 def worldx(self, x):
\r
1141 return (x - self.offsetx) / self.signx
\r
1143 def worldy(self, y):
\r
1144 return (y - self.offsety) / self.signy
\r
1146 def changesize(self, top, bottom, left, right):
\r
1147 oldoffsetx = max(-left, 0)
\r
1148 newoffsetx = max(left, 0)
\r
1149 newwidth = self.width + left + right
\r
1150 oldoffsety = max(-top, 0)
\r
1151 newoffsety = max(top, 0)
\r
1152 newheight = self.height + top + bottom
\r
1154 newdata = self.initialdata(newwidth + 1, newheight + 1)
\r
1156 for x in range(min(self._width, newwidth) + 1):
\r
1157 for y in range(min(self._height, newheight) + 1):
\r
1158 for d in ["h", "v"]:
\r
1159 newdata[x+newoffsetx][y+newoffsety][d] = self._data[x+oldoffsetx][y+oldoffsety][d]
\r
1160 newnote[self.coodtokey(x+newoffsetx, y+newoffsety)] = self.getnote(x+oldoffsetx, y+oldoffsety)
\r
1161 self._width = newwidth
\r
1162 self._height = newheight
\r
1163 self.setoffset(self.offsetx -self.signx * left, self.offsety -self.signy * top)
\r
1164 self._data = newdata
\r
1165 self._note = newnote
\r
1167 def growwall(self, x1, y1, x2, y2, eraseonly, alwaysbox):
\r
1168 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1169 offsetx, offsety = self.getoffset(x1, y1, x2, y2)
\r
1171 #delete inner walls.
\r
1172 for x in range(x1, x2+stepx, stepx):
\r
1173 for y in range(y1+stepy+offsety, y2+stepy+offsety, stepy):
\r
1174 self._data[x][y]["h"] = 0
\r
1175 for x in range(x1+stepx+offsetx, x2+stepx+offsetx, stepx):
\r
1176 for y in range(y1, y2+stepy, stepy):
\r
1177 self._data[x][y]["v"] = 0
\r
1180 #draw OUTER wall if it exists.
\r
1181 if alwaysbox or (x1 == x2 and y1 == y2):
\r
1194 for x in range(x1, x2+stepx, stepx):
\r
1195 if not (vline and x == x1):
\r
1197 if self._data[x][y1+offsety]["h"] == 0:
\r
1198 self._data[x][y1+offsety]["h"] = 1
\r
1199 if self._data[x][y2+stepy+offsety]["h"] == 0:
\r
1200 self._data[x][y2+stepy+offsety]["h"] = 1
\r
1201 for y in range(y1, y2+stepy, stepy):
\r
1202 if not (hline and y == y1):
\r
1204 if self._data[x1+offsetx][y]["v"] == 0:
\r
1205 self._data[x1+offsetx][y]["v"] = 1
\r
1206 if self._data[x2+stepx+offsetx][y]["v"] == 0:
\r
1207 self._data[x2+stepx+offsetx][y]["v"] = 1
\r
1209 def fillbackcolor(self, x1, y1, x2, y2, backcolor):
\r
1210 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1211 for x in range(x1, x2+stepx, stepx):
\r
1212 for y in range(y1, y2+stepy, stepy):
\r
1213 self.setbackcolor(x, y, backcolor)
\r
1215 def getstep(self, x1, y1, x2, y2):
\r
1224 return (stepx, stepy)
\r
1226 def getoffset(self, x1, y1, x2, y2):
\r
1235 return (offsetx, offsety)
\r
1237 def save(self, filename):
\r
1238 dt = self.savestring()
\r
1239 with open(filename, "w", encoding='utf-8') as f:
\r
1241 self.filename = filename
\r
1243 def savestring(self):
\r
1245 data["size"] = {"x":self.width, "y":self.height}
\r
1246 data["offset"] = {"x":self.offsetx, "y":self.offsety}
\r
1247 data["sign"] = {"x":self.signx, "y":self.signy}
\r
1248 data["map"] = self.getmapstring()
\r
1252 for nk, ni in list(self._note.items()):
\r
1253 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"]:
\r
1254 x, y = self.keytocood(nk)
\r
1255 n[self.coodtokey(self.viewx(x), self.viewy(y))] = ni
\r
1257 return yaml.safe_dump(data, allow_unicode=True,
\r
1258 default_flow_style=False, encoding='utf-8').decode('utf-8')
\r
1260 def getmapstring(self):
\r
1263 for y in range(self.height):
\r
1265 for x in range(self.width):
\r
1267 s.append(self.hwall[self._data[x][y]["h"]])
\r
1270 m.append("".join(s))
\r
1272 for x in range(self.width):
\r
1273 s.append(self.vwall[self._data[x][y]["v"]])
\r
1275 s.append(self.vwall[self._data[self.width][y]["v"]])
\r
1277 m.append("".join(s))
\r
1280 for x in range(self.width):
\r
1282 s.append(self.hwall[self._data[x][y]["h"]])
\r
1285 m.append("".join(s))
\r
1288 def load(self, filename):
\r
1289 with open(filename, "r", encoding="utf-8") as f:
\r
1291 self.loadfromstring(st)
\r
1292 self.filename = filename
\r
1294 def loadfromstring(self, st):
\r
1295 data = yaml.safe_load(st)
\r
1298 self._width = data["size"]["x"]
\r
1299 self._height = data["size"]["y"]
\r
1300 self._signx = data["sign"]["x"]
\r
1301 self._signy = data["sign"]["y"]
\r
1302 self._offsetx = data["offset"]["x"]
\r
1303 self._offsety = data["offset"]["y"]
\r
1307 for y in range(self.height):
\r
1308 for x in range(self.width):
\r
1309 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1310 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1312 for y in range(self.height):
\r
1313 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1315 for x in range(self.width):
\r
1316 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1320 for nk, ni in list(data["note"].items()):
\r
1321 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":
\r
1322 x, y = self.keytocood(nk)
\r
1323 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni
\r
1328 class UndoManager(QtCore.QObject):
\r
1329 MAX_UNDO_COUNT = 128
\r
1330 changed = QtCore.Signal(bool, bool)
\r
1332 def __init__(self):
\r
1333 super(UndoManager, self).__init__()
\r
1337 self._undo = [None for x in range(self.MAX_UNDO_COUNT)]
\r
1339 self._undocount = 0
\r
1340 self._commited = True
\r
1341 self.changed.emit(self.canundo, self.canredo)
\r
1343 def init(self, data):
\r
1346 self._commited = True
\r
1348 def save(self, obj):
\r
1349 if self._index >= self.MAX_UNDO_COUNT:
\r
1350 self._undo = self._undo[1:]
\r
1352 self._undo.append(None)
\r
1353 self._undo[self._index] = obj
\r
1355 self._undocount = 0
\r
1356 self._commited = False
\r
1357 self.changed.emit(self.canundo, self.canredo)
\r
1361 self._undocount += 1
\r
1362 self._commited = False
\r
1363 self.changed.emit(self.canundo, self.canredo)
\r
1364 return self._undo[self._index - 1]
\r
1368 self._undocount -= 1
\r
1369 self._commited = False
\r
1370 self.changed.emit(self.canundo, self.canredo)
\r
1371 return self._undo[self._index - 1]
\r
1374 self._commited = True
\r
1377 def canundo(self):
\r
1378 return (self._index > 1)
\r
1381 def canredo(self):
\r
1382 return (self._undocount > 0)
\r
1385 def commited(self):
\r
1386 return self._commited
\r
1389 class PydunColorDialog(QtGui.QColorDialog):
\r
1390 def __init__(self, parent, config):
\r
1391 super(PydunColorDialog, self).__init__(parent)
\r
1392 for index in range(self.customCount()):
\r
1393 self.setCustomColor(index,
\r
1394 getcolorfromstring(
\r
1395 config.get(index, "#FFFFFF")).rgb())
\r
1396 self.updateconfig()
\r
1398 def updateconfig(self):
\r
1399 self._config = dict()
\r
1400 for index in range(self.customCount()):
\r
1401 self._config[index] = getcolorstring(
\r
1402 QtGui.QColor.fromRgb(self.customColor(index)))
\r
1405 super(PydunColorDialog, self).exec_()
\r
1406 self.updateconfig()
\r
1410 return self._config
\r
1413 class PydunAskSaveDialog(QtGui.QMessageBox):
\r
1414 def __init__(self, parent, filename):
\r
1415 super(PydunAskSaveDialog, self).__init__(parent)
\r
1416 self.setText("{filename} への変更を保存しますか?".format(filename=filename))
\r
1417 self.setStandardButtons(QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel)
\r
1418 self.setDefaultButton(QtGui.QMessageBox.Save)
\r
1419 self.button(QtGui.QMessageBox.Save).setText("保存する(&S)")
\r
1420 self.button(QtGui.QMessageBox.Discard).setText("保存しない(&N)")
\r
1421 self.button(QtGui.QMessageBox.Cancel).setText("キャンセル")
\r
1424 def getcolorstring(color):
\r
1425 return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())
\r
1427 def getcolorfromstring(colorstring):
\r
1428 return QtGui.QColor.fromRgb(
\r
1429 int(colorstring[1:3], 16),
\r
1430 int(colorstring[3:5], 16),
\r
1431 int(colorstring[5:7], 16))
\r
1434 return os.path.dirname(os.path.abspath(sys.argv[0]))
\r
1436 def getlatestversion():
\r
1438 rss = urllib.request.urlopen(projectrssurl)
\r
1439 rssstring = rss.read()
\r
1440 rsstree = xml.etree.ElementTree.fromstring(rssstring)
\r
1441 item = rsstree.find("channel/item/title")
\r
1442 ver = (item.text.split(" "))[2]
\r
1445 ver = projectversion
\r
1451 app = QtGui.QApplication(sys.argv)
\r
1452 mainWin = MainWindow()
\r
1453 app.installEventFilter(mainWin.centralWidget().mapframe)
\r
1455 sys.exit(app.exec_())
\r
1459 global configfilename
\r
1460 configfilename = os.path.join(
\r
1464 with open(configfilename, "r") as f:
\r
1465 config = yaml.safe_load(f)
\r
1469 if __name__ == '__main__':
\r