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 PySide2 import QtCore, QtGui, QtWidgets
\r
26 projecturl = "http://ja.osdn.net/projects/pydun/"
\r
27 projectrssurl = "http://ja.osdn.net/projects/pydun/releases/rss"
\r
28 projectversion = "1.2.1"
\r
29 defaultfontname = "Yu Gothic UI"
\r
31 class MainWindow(QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.QAction("マップのサイズ(&S)", self)
\r
103 setmapsizeact.triggered.connect(self.setmapsize_triggered)
\r
104 editmenu.addAction(setmapsizeact)
\r
105 setorigineact = QtWidgets.QAction("座標設定(&O)", self)
\r
106 setorigineact.triggered.connect(self.setorigine_triggered)
\r
107 editmenu.addAction(setorigineact)
\r
108 wallmenustringact = QtWidgets.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 = QtWidgets.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 = QtWidgets.QAction("プロジェクトのWebサイト(&W)", self)
\r
121 projectact.triggered.connect(self.project_triggered)
\r
122 helpmenu.addAction(projectact)
\r
123 aboutact = QtWidgets.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 == QtWidgets.QMessageBox.Cancel:
\r
172 elif ret == QtWidgets.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 = QtWidgets.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 = QtWidgets.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 QtWidgets.QMessageBox.information(
\r
271 self, title, "座標設定を中止します。", QtWidgets.QMessageBox.Ok)
\r
272 self.mainframe.mapframe.setoriginemode = False
\r
274 if QtWidgets.QMessageBox.Ok == QtWidgets.QMessageBox.information(
\r
275 self, title, "基準にする地点をクリックしてください。",
\r
276 (QtWidgets.QMessageBox.Ok| QtWidgets.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() == QtWidgets.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 QtWidgets.QMessageBox.information(
\r
295 self, "壁メニューに文字を表示する", "表示の切替は再起動後に有効になります。",
\r
296 (QtWidgets.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 QtWidgets.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, PySide2, PyYAML "
\r
319 "これらの作成者に深く感謝いたします。</p>"
\r
320 "<p>詳細はLICENCE.txtを参照してください。</p>")
\r
323 class MainFrame(QtWidgets.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 f = QtGui.QFont(defaultfontname, 9)
\r
332 self.mapframe = MapFrame(self)
\r
333 scrollarea = QtWidgets.QScrollArea(self)
\r
334 scrollarea.setWidget(self.mapframe)
\r
336 self.detail = QtWidgets.QLabel(self)
\r
337 self.detail.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
338 self.detail.setText("")
\r
339 self.detail.setMaximumHeight(100)
\r
340 self.detail.setMinimumHeight(100)
\r
342 self.boxdrawbutton = QtWidgets.QRadioButton(self)
\r
343 self.boxdrawbutton.setText("ボックス形式で壁を描画(&B)")
\r
344 self.boxdrawbutton.setChecked(True)
\r
345 self.boxdrawbutton.setSizePolicy(
\r
346 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
348 self.growdrawbutton = QtWidgets.QRadioButton(self)
\r
349 self.growdrawbutton.setText("足跡形式で壁を描画(&G)")
\r
350 self.growdrawbutton.setChecked(False)
\r
351 self.growdrawbutton.setSizePolicy(
\r
352 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
354 self.backcolorbutton = QtWidgets.QRadioButton(self)
\r
355 self.backcolorbutton.setText("背景色(&C)")
\r
356 self.backcolorbutton.setChecked(False)
\r
357 self.backcolorbutton.setSizePolicy(
\r
358 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
360 self.setbackcolorbutton = QtWidgets.QPushButton(self)
\r
361 self.setbackcolorbutton.setText("背景色を設定(&S)...")
\r
362 self.setbackcolorbutton.setSizePolicy(
\r
363 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
365 self.backcolorbox = ColorBox(self)
\r
366 self.backcolorbox.setMinimumSize(30, 30)
\r
367 self.backcolorbox.setSizePolicy(
\r
368 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
370 latestversion = getlatestversion()
\r
371 if latestversion != projectversion:
\r
372 self.update = QtWidgets.QLabel(self)
\r
373 self.update.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
\r
374 self.update.setText("<a href='{url}'>最新のPydun({ver})がダウンロードできます。</a>".format(url=projecturl, ver=latestversion))
\r
375 self.update.setOpenExternalLinks(True)
\r
377 layout = QtWidgets.QGridLayout(self)
\r
378 layout.addWidget(scrollarea, 0, 0, 1, 3)
\r
379 layout.addWidget(self.detail, 1, 0, 4, 1)
\r
380 layout.addWidget(self.boxdrawbutton, 1, 1, 1, 2)
\r
381 layout.addWidget(self.growdrawbutton, 2, 1, 1, 2)
\r
382 layout.addWidget(self.backcolorbutton, 3, 1, 1, 2)
\r
383 layout.addWidget(self.setbackcolorbutton, 4, 1, 1, 1)
\r
384 layout.addWidget(self.backcolorbox, 4, 2, 1, 1)
\r
385 if latestversion != projectversion:
\r
386 layout.addWidget(self.update, 5, 0, 1, 3)
\r
388 self.setLayout(layout)
\r
390 self.h_wall_menu = self.create_wall_menu("h")
\r
391 self.v_wall_menu = self.create_wall_menu("v")
\r
393 self.mapframe.mouse_moved.connect(self.mouse_moved)
\r
394 self.mapframe.mouse_released.connect(self.mouse_released)
\r
395 self.mapframe.mouse_drag_released.connect(self.mouse_drag_released)
\r
396 self.create_wall_menu_triggered_signal.connect(
\r
397 self.create_wall_menu_triggered)
\r
398 self.setbackcolorbutton.clicked.connect(
\r
399 self.setbackcolorbutton_clicked)
\r
401 def create_wall_menu(self, direction):
\r
402 menu = QtWidgets.QMenu(self)
\r
403 for idx, img in enumerate(_mapimages.wall_icons):
\r
404 act = QtWidgets.QAction(_mapimages.wall_texts[idx][direction], self)
\r
405 act.setIcon(img[direction])
\r
409 self.create_wall_menu_triggered_signal.emit(menu.x, menu.y, direction, idx)
\r
412 act.triggered.connect(triggerd(idx))
\r
413 menu.addAction(act)
\r
416 @QtCore.Slot(int, int, int)
\r
417 def mouse_moved(self, x=0, y=0, b=QtCore.Qt.MouseButton.NoButton):
\r
418 cood = "({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))
\r
419 self.detail.setText(cood + _mapengine.getdetail(x, y))
\r
420 self.mapframe.repaint()
\r
422 @QtCore.Slot(int, int, int, int, int)
\r
423 def mouse_drag_released(self, x1, y1, x2, y2, eraseonly):
\r
424 if self.boxdrawbutton.isChecked():
\r
425 _mapengine.growwall(x1, y1, x2, y2, eraseonly, True)
\r
426 elif self.growdrawbutton.isChecked():
\r
427 _mapengine.growwall(x1, y1, x2, y2, eraseonly, False)
\r
428 elif self.backcolorbutton.isChecked():
\r
432 backcolor = getcolorstring(self.backcolorbox.color)
\r
433 _mapengine.fillbackcolor(x1, y1, x2, y2, backcolor)
\r
434 _undomanager.save(_mapengine.savestring())
\r
435 self.mapframe.repaint()
\r
437 @QtCore.Slot(int, int, str)
\r
438 def mouse_released(self, x1, y1, direction):
\r
440 if self.mapframe.setoriginemode:
\r
441 dlg = SetOrigineDialog(self)
\r
442 dlg.setcurrent(_mapengine.viewx(x1), _mapengine.viewy(y1), _mapengine.signx, _mapengine.signy)
\r
443 dlg.exec_() #showでは処理がとまらない。
\r
444 if dlg.result() == QtWidgets.QDialog.Accepted:
\r
445 _mapengine.setsign(dlg.signx, dlg.signy)
\r
446 _mapengine.setoffset(
\r
447 dlg.originex - _mapengine.viewx(x1) + _mapengine.offsetx,
\r
448 dlg.originey - _mapengine.viewy(y1) + _mapengine.offsety
\r
450 _undomanager.save(_mapengine.savestring())
\r
451 self.mapframe.setoriginemode = False
\r
454 if direction == "c":
\r
455 dlg = DetailDialog(self)
\r
456 dlg.setvalue(_mapengine.viewx(x1), _mapengine.viewy(y1),
\r
457 _mapengine.getmark(x1, y1), _mapengine.getdetail(x1, y1),
\r
458 getcolorfromstring(_mapengine.getforecolor(x1, y1)))
\r
459 dlg.exec_() #showでは処理がとまらない。
\r
460 if dlg.result() == QtWidgets.QDialog.Accepted:
\r
461 forecolor = getcolorstring(dlg.forecolorbox.color)
\r
462 _mapengine.setmark(x1, y1, dlg.marktext.text())
\r
463 _mapengine.setdetail(x1, y1, dlg.detailtext.toPlainText())
\r
464 _mapengine.setforecolor(x1, y1, forecolor)
\r
465 _undomanager.save(_mapengine.savestring())
\r
466 self.mapframe.repaint()
\r
470 if direction == "h":
\r
471 menu = self.h_wall_menu
\r
472 elif direction == "v":
\r
473 menu = self.v_wall_menu
\r
476 menu.popup(QtGui.QCursor.pos())
\r
478 @QtCore.Slot(int, int, str, int)
\r
479 def create_wall_menu_triggered(self, x1, y1, direction, wall):
\r
480 _mapengine.setdata(x1, y1, direction, wall)
\r
481 _undomanager.save(_mapengine.savestring())
\r
482 self.mapframe.repaint()
\r
485 def setbackcolorbutton_clicked(self):
\r
487 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
488 dlg.setCurrentColor(self.backcolorbox.color)
\r
490 config["customColor"] = dlg.config
\r
491 if dlg.result() == QtWidgets.QDialog.Accepted:
\r
492 self.backcolorbox.color = dlg.currentColor()
\r
493 self.backcolorbutton.setChecked(True)
\r
496 class MapFrame(QtWidgets.QFrame):
\r
497 mouse_moved = QtCore.Signal(int, int, int)
\r
498 mouse_released = QtCore.Signal(int, int, str)
\r
499 mouse_drag_released = QtCore.Signal(int, int, int, int, int)
\r
503 def __init__(self, parent=None):
\r
504 super(MapFrame, self).__init__(parent)
\r
505 self.setFont = QtGui.QFont(defaultfontname, 9)
\r
506 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
515 self._dragging = False
\r
516 self.setoriginemode = False
\r
517 self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
519 _mapimages.width * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
520 _mapimages.height * (_mapengine.height) + _mapimages.heightoffset * 2
\r
523 def paintEvent(self, event):
\r
524 painter = QtGui.QPainter(self)
\r
525 painter.fillRect(0, 0, self.width(), self.height(), QtGui.QColor(255, 255, 255))
\r
526 w = _mapimages.width - 1
\r
527 v = _mapimages.height - 1
\r
528 ho = _mapimages.heightoffset
\r
529 wo = _mapimages.widthoffset
\r
533 w * (_mapengine.width) + _mapimages.widthoffset * 2,
\r
534 v * (_mapengine.height) + _mapimages.heightoffset * 2
\r
538 for x in range(_mapengine.width):
\r
540 for y in range(_mapengine.height):
\r
542 backcolor = _mapengine.getbackcolor(x, y)
\r
544 painter.fillRect(wo + xx, ho + yy, w, v,
\r
545 getcolorfromstring(backcolor))
\r
548 for x in range(_mapengine.width + 1):
\r
550 for y in range(_mapengine.height + 1):
\r
552 if x != _mapengine.width:
\r
553 painter.drawImage(wo + xx, yy,
\r
554 _mapimages.wall(0, "h"))
\r
555 if y != _mapengine.height:
\r
556 painter.drawImage(xx, ho + yy,
\r
557 _mapimages.wall(0, "v"))
\r
560 for x in range(_mapengine.width + 1):
\r
562 for y in range(_mapengine.height + 1):
\r
564 if x != _mapengine.width and _mapengine.getdata(x, y, "h") != 0:
\r
565 painter.drawImage(wo + xx, yy,
\r
566 _mapimages.wall(_mapengine.getdata(x, y, "h"), "h"))
\r
567 if y != _mapengine.height and _mapengine.getdata(x, y, "v") != 0:
\r
568 painter.drawImage(xx, ho + yy,
\r
569 _mapimages.wall(_mapengine.getdata(x, y, "v"), "v"))
\r
570 mark = _mapengine.getmark(x, y)
\r
572 painter.setPen(getcolorfromstring(_mapengine.getforecolor(x, y)))
\r
573 painter.drawText(wo + xx + 2, ho + yy + 2, w - 2, v - 2,
\r
574 QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter,
\r
577 #座標設定中はdrawing box を表示しない。
\r
578 if self.setoriginemode:
\r
582 if self._pressedbutton != QtCore.Qt.MouseButton.NoButton:
\r
583 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
584 if self._x1 == self._x2 and self._y1 == self._y2:
\r
585 painter.setPen(QtGui.QColor(255, 0, 0))
\r
586 elif self._x1 == self._x2 or self._y1 == self._y2:
\r
587 painter.setPen(QtGui.QColor(0, 255, 0))
\r
589 painter.setPen(QtGui.QColor(255, 0, 0))
\r
590 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
591 painter.setPen(QtGui.QColor(0, 0, 255))
\r
592 painter.drawRect(self._px1, self._py1,
\r
593 self._px2 - self._px1, self._py2 - self._py1)
\r
595 def eventFilter(self, obj, event):
\r
597 return ((event.pos().x() - _mapimages.widthoffset) // (_mapimages.width - 1))
\r
600 return ((event.pos().y() - _mapimages.heightoffset) // (_mapimages.height - 1))
\r
605 if et == QtCore.QEvent.MouseButtonPress:
\r
608 self._pos1 = event.pos()
\r
609 self._px1 = event.pos().x()
\r
610 self._py1 = event.pos().y()
\r
613 self._px2 = event.pos().x()
\r
614 self._py2 = event.pos().y()
\r
615 self._pressedbutton = event.buttons()
\r
616 self._dragging = False
\r
619 elif et == QtCore.QEvent.MouseMove:
\r
622 self._px2 = event.pos().x()
\r
623 self._py2 = event.pos().y()
\r
624 if (self._pressedbutton != QtCore.Qt.MouseButton.NoButton and
\r
625 (event.pos() - self._pos1).manhattanLength() >=
\r
626 QtWidgets.QApplication.startDragDistance()):
\r
627 self._dragging = True
\r
628 self.mouse_moved.emit(self._x2, self._y2, event.buttons())
\r
631 elif et == QtCore.QEvent.MouseButtonRelease:
\r
633 release_emit = False
\r
636 if self._pressedbutton == QtCore.Qt.MouseButton.LeftButton:
\r
638 elif self._pressedbutton == QtCore.Qt.MouseButton.RightButton:
\r
641 release_emit = True
\r
642 if self.setoriginemode:
\r
643 release_emit = True
\r
645 self._pressedbutton = QtCore.Qt.MouseButton.NoButton
\r
646 self._dragging = False
\r
648 self.mouse_drag_released.emit(
\r
649 self._x1, self._y1, self._x2, self._y2, eraseonly)
\r
651 rpx = self._px2 - self._x2 * (_mapimages.width - 1) - _mapimages.widthoffset
\r
652 rpy = self._py2 - self._y2 * (_mapimages.height - 1) - _mapimages.heightoffset
\r
653 rdx = rpx - (_mapimages.width - 1) // 2
\r
654 rdy = rpy - (_mapimages.height - 1) // 2
\r
655 if rpx <= _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
659 elif rpx >= _mapimages.width - _mapimages.widthoffset and abs(rdx) > abs(rdy):
\r
663 elif rpy <= _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
667 elif rpy >= _mapimages.height - _mapimages.heightoffset and abs(rdx) <= abs(rdy):
\r
675 self.mouse_released.emit(rx, ry, d)
\r
681 # pass the event on to the parent class
\r
685 class ColorBox(QtWidgets.QFrame):
\r
686 def __init__(self, parent=None):
\r
687 super(ColorBox, self).__init__(parent)
\r
688 self.color = QtGui.QColor(255, 255, 255)
\r
689 self.bordercolor = QtGui.QColor(0, 0, 0)
\r
691 def paintEvent(self, event):
\r
692 painter = QtGui.QPainter(self)
\r
693 painter.fillRect(0, 0, self.width(), self.height(), self.color)
\r
694 painter.setPen(self.bordercolor)
\r
695 painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
\r
698 class DetailDialog(QtWidgets.QDialog):
\r
699 def __init__(self, parent=None):
\r
700 super(DetailDialog, self).__init__(parent)
\r
702 f = QtGui.QFont(defaultfontname, 9)
\r
705 marklabel = QtWidgets.QLabel(self)
\r
706 marklabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
707 marklabel.setText("マーク(&M)")
\r
708 marklabel.setSizePolicy(
\r
709 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
711 self.marktext = QtWidgets.QLineEdit(self)
\r
712 self.marktext.setFont(f)
\r
713 self.marktext.setMaxLength(2)
\r
714 self.marktext.setText("")
\r
715 self.marktext.setMinimumWidth(20)
\r
716 self.marktext.setSizePolicy(
\r
717 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
718 marklabel.setBuddy(self.marktext)
\r
720 self.forecolorbutton = QtWidgets.QPushButton(self)
\r
721 self.forecolorbutton.setText("文字色(&C)...")
\r
722 self.forecolorbutton.clicked.connect(self.forecolorbutton_clicked)
\r
724 self.forecolorbox = ColorBox(self)
\r
725 self.forecolorbox.setMinimumSize(30, 30)
\r
726 self.forecolorbox.setSizePolicy(
\r
727 QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
\r
729 detaillabel = QtWidgets.QLabel(self)
\r
730 detaillabel.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight)
\r
731 detaillabel.setText("詳細(&D)")
\r
733 self.detailtext = QtWidgets.QTextEdit(self)
\r
734 self.detailtext.setText("")
\r
735 detaillabel.setBuddy(self.detailtext)
\r
737 self.buttonbox = QtWidgets.QDialogButtonBox(
\r
738 QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
\r
739 self.buttonbox.accepted.connect(self.accept)
\r
740 self.buttonbox.rejected.connect(self.reject)
\r
741 self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).setText("OK")
\r
742 self.buttonbox.button(QtWidgets.QDialogButtonBox.Cancel).setText("キャンセル")
\r
744 layout = QtWidgets.QGridLayout()
\r
745 layout.addWidget(marklabel, 0, 0, 1, 1)
\r
746 layout.addWidget(self.marktext, 0, 1, 1, 1)
\r
747 layout.addWidget(self.forecolorbutton, 0, 2, 1, 1)
\r
748 layout.addWidget(self.forecolorbox, 0, 3, 1, 1)
\r
749 layout.addWidget(detaillabel, 1, 0, 1, 1)
\r
750 layout.addWidget(self.detailtext, 1, 1, 1, 3)
\r
751 layout.addWidget(self.buttonbox, 2, 0, 1, 4)
\r
752 self.setLayout(layout)
\r
753 self.setModal(True)
\r
755 def setvalue(self, x, y, mark, detail, color):
\r
756 self.setWindowTitle("({x}, {y})".format(x=x, y=y))
\r
757 self.marktext.setText(mark)
\r
758 self.detailtext.setText(detail)
\r
759 self.forecolorbox.color = color
\r
761 def forecolorbutton_clicked(self):
\r
763 dlg = PydunColorDialog(self, config.get("customColor", dict()))
\r
764 dlg.setCurrentColor(self.forecolorbox.color)
\r
766 config["customColor"] = dlg.config
\r
767 if dlg.result() == QtWidgets.QDialog.Accepted:
\r
768 self.forecolorbox.color = dlg.currentColor()
\r
771 class SetOrigineDialog(QtWidgets.QDialog):
\r
772 def __init__(self, parent=None):
\r
773 super(SetOrigineDialog, self).__init__(parent)
\r
775 f = QtGui.QFont(defaultfontname, 9)
\r
778 self.setWindowTitle("座標設定")
\r
780 promptlabel = QtWidgets.QLabel(self)
\r
781 promptlabel.setAlignment(
\r
782 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
783 promptlabel.setText("この地点の座標と軸の方向を入力してください。")
\r
785 self.currentlabel = QtWidgets.QLabel(self)
\r
786 self.currentlabel.setAlignment(
\r
787 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
\r
788 self.currentlabel.setText("")
\r
790 xlabel = QtWidgets.QLabel(self)
\r
791 xlabel.setAlignment(
\r
792 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
793 xlabel.setText("&X")
\r
795 self.xbox = QtWidgets.QSpinBox(self)
\r
796 self.xbox.setRange(-999, +999)
\r
797 self.xbox.setSingleStep(1)
\r
798 self.xbox.setValue(0)
\r
799 xlabel.setBuddy(self.xbox)
\r
801 ylabel = QtWidgets.QLabel(self)
\r
802 ylabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
\r
803 ylabel.setText("&Y")
\r
805 self.ybox = QtWidgets.QSpinBox(self)
\r
806 self.ybox.setRange(-999, +999)
\r
807 self.ybox.setSingleStep(1)
\r
808 self.ybox.setValue(0)
\r
809 ylabel.setBuddy(self.ybox)
\r
811 self.xplusbutton = QtWidgets.QRadioButton(self)
\r
812 self.xplusbutton.setText("右が正(&R)")
\r
813 self.xminusbutton = QtWidgets.QRadioButton(self)
\r
814 self.xminusbutton.setText("左が正(&L)")
\r
815 self.yminusbutton = QtWidgets.QRadioButton(self)
\r
816 self.yminusbutton.setText("上が正(&T)")
\r
817 self.yplusbutton = QtWidgets.QRadioButton(self)
\r
818 self.yplusbutton.setText("下が正(&B)")
\r
820 xgroup = QtWidgets.QButtonGroup(self)
\r
821 xgroup.addButton(self.xplusbutton)
\r
822 xgroup.addButton(self.xminusbutton)
\r
824 ygroup = QtWidgets.QButtonGroup(self)
\r
825 ygroup.addButton(self.yplusbutton)
\r
826 ygroup.addButton(self.yminusbutton)
\r
828 self.buttonbox = QtWidgets.QDialogButtonBox(
\r
829 QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
\r
830 self.buttonbox.accepted.connect(self.accept)
\r
831 self.buttonbox.rejected.connect(self.reject)
\r
832 self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).setText("OK")
\r
833 self.buttonbox.button(QtWidgets.QDialogButtonBox.Cancel).setText("キャンセル")
\r
835 layout = QtWidgets.QGridLayout()
\r
836 layout.addWidget(promptlabel, 0, 0, 1, 4)
\r
837 layout.addWidget(self.currentlabel, 1, 0, 1, 4)
\r
838 layout.addWidget(xlabel, 2, 0, 1, 1)
\r
839 layout.addWidget(self.xbox, 2, 1, 1, 1)
\r
840 layout.addWidget(ylabel, 2, 2, 1, 1)
\r
841 layout.addWidget(self.ybox, 2, 3, 1, 1)
\r
842 layout.addWidget(self.xplusbutton, 3, 1, 1, 1)
\r
843 layout.addWidget(self.xminusbutton, 4, 1, 1, 1)
\r
844 layout.addWidget(self.yminusbutton, 3, 3, 1, 1)
\r
845 layout.addWidget(self.yplusbutton, 4, 3, 1, 1)
\r
846 layout.addWidget(self.buttonbox, 5, 0, 1, 4)
\r
847 self.setLayout(layout)
\r
848 self.setModal(True)
\r
850 def setcurrent(self, x, y, signx, signy):
\r
851 self.xbox.setValue(x)
\r
852 self.ybox.setValue(y)
\r
853 self.currentlabel.setText("現在の座標 ({x}, {y})".format(x=x, y=y))
\r
855 self.xplusbutton.setChecked(True)
\r
857 self.xminusbutton.setChecked(True)
\r
859 self.yplusbutton.setChecked(True)
\r
861 self.yminusbutton.setChecked(True)
\r
864 def originex(self):
\r
865 return self.xbox.value()
\r
868 def originey(self):
\r
869 return self.ybox.value()
\r
873 if self.xplusbutton.isChecked():
\r
875 elif self.xminusbutton.isChecked():
\r
882 if self.yplusbutton.isChecked():
\r
884 elif self.yminusbutton.isChecked():
\r
890 class SetSizeDialog(QtWidgets.QDialog):
\r
891 def __init__(self, parent=None):
\r
892 super(SetSizeDialog, self).__init__(parent)
\r
894 f = QtGui.QFont(defaultfontname, 9)
\r
897 self.setWindowTitle("マップのサイズ")
\r
899 self.topbutton = QtWidgets.QRadioButton(self)
\r
900 self.topbutton.setText("上(&T)")
\r
901 self.topbutton.clicked.connect(self.updatewidgets)
\r
903 self.topsize = QtWidgets.QSpinBox(self)
\r
904 self.topsize.setSingleStep(1)
\r
905 self.topsize.setValue(0)
\r
906 self.topsize.valueChanged.connect(self.updatewidgets)
\r
908 self.bottombutton = QtWidgets.QRadioButton(self)
\r
909 self.bottombutton.setText("下(&B)")
\r
910 self.bottombutton.clicked.connect(self.updatewidgets)
\r
912 self.bottomsize = QtWidgets.QSpinBox(self)
\r
913 self.bottomsize.setSingleStep(1)
\r
914 self.bottomsize.setValue(0)
\r
915 self.bottomsize.valueChanged.connect(self.updatewidgets)
\r
917 self.leftbutton = QtWidgets.QRadioButton(self)
\r
918 self.leftbutton.setText("左(&L)")
\r
919 self.leftbutton.clicked.connect(self.updatewidgets)
\r
921 self.leftsize = QtWidgets.QSpinBox(self)
\r
922 self.leftsize.setSingleStep(1)
\r
923 self.leftsize.setValue(0)
\r
924 self.leftsize.valueChanged.connect(self.updatewidgets)
\r
926 self.rightbutton = QtWidgets.QRadioButton(self)
\r
927 self.rightbutton.setText("右(&R)")
\r
928 self.rightbutton.clicked.connect(self.updatewidgets)
\r
930 self.rightsize = QtWidgets.QSpinBox(self)
\r
931 self.rightsize.setSingleStep(1)
\r
932 self.rightsize.setValue(0)
\r
933 self.rightsize.valueChanged.connect(self.updatewidgets)
\r
935 self.sizelabel = QtWidgets.QLabel(self)
\r
936 self.sizelabel .setAlignment(
\r
937 QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
\r
938 self.sizelabel.setText("この地点の座標を入力してください。")
\r
940 self.buttonbox = QtWidgets.QDialogButtonBox(
\r
941 QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
\r
942 self.buttonbox.accepted.connect(self.accept)
\r
943 self.buttonbox.rejected.connect(self.reject)
\r
944 self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).setText("OK")
\r
945 self.buttonbox.button(QtWidgets.QDialogButtonBox.Cancel).setText("キャンセル")
\r
947 verticalgroup = QtWidgets.QButtonGroup(self)
\r
948 verticalgroup.addButton(self.topbutton)
\r
949 verticalgroup.addButton(self.bottombutton)
\r
951 holizontalgroup = QtWidgets.QButtonGroup(self)
\r
952 holizontalgroup.addButton(self.leftbutton)
\r
953 holizontalgroup.addButton(self.rightbutton)
\r
955 self.topbutton.setChecked(True)
\r
956 self.bottombutton.setChecked(False)
\r
957 self.leftbutton.setChecked(True)
\r
958 self.rightbutton.setChecked(False)
\r
960 layout = QtWidgets.QGridLayout(self)
\r
961 layout.addWidget(self.topbutton, 0, 2, 1, 1)
\r
962 layout.addWidget(self.topsize, 0, 3, 1, 1)
\r
963 layout.addWidget(self.leftbutton, 1, 0, 1, 1)
\r
964 layout.addWidget(self.leftsize, 1, 1, 1, 1)
\r
965 layout.addWidget(self.sizelabel, 1, 2, 1, 2)
\r
966 layout.addWidget(self.rightbutton, 1, 4, 1, 1)
\r
967 layout.addWidget(self.rightsize, 1, 5, 1, 1)
\r
968 layout.addWidget(self.bottombutton, 2, 2, 1, 1)
\r
969 layout.addWidget(self.bottomsize, 2, 3, 1, 1)
\r
970 layout.addWidget(self.buttonbox, 3, 0, 1, 6)
\r
971 self.setLayout(layout)
\r
972 self.setModal(True)
\r
974 def setoriginalsize(self, width, height):
\r
975 self._width = width
\r
976 self._height = height
\r
977 self.topsize.setRange(-height+1, +100)
\r
978 self.bottomsize.setRange(-height+1, +100)
\r
979 self.leftsize.setRange(-width+1, +100)
\r
980 self.rightsize.setRange(-width+1, +100)
\r
981 self.updatewidgets()
\r
983 def updatewidgets(self):
\r
987 if self.topbutton.isChecked():
\r
988 dh = self.topsize.value()
\r
989 self.topsize.setEnabled(True)
\r
990 self.bottomsize.setDisabled(True)
\r
991 elif self.bottombutton.isChecked():
\r
992 dh = self.bottomsize.value()
\r
993 self.topsize.setDisabled(True)
\r
994 self.bottomsize.setEnabled(True)
\r
995 if self.leftbutton.isChecked():
\r
996 dw = self.leftsize.value()
\r
997 self.leftsize.setEnabled(True)
\r
998 self.rightsize.setDisabled(True)
\r
999 elif self.rightbutton.isChecked():
\r
1000 dw = self.rightsize.value()
\r
1001 self.leftsize.setDisabled(True)
\r
1002 self.rightsize.setEnabled(True)
\r
1004 self.sizelabel.setText(
\r
1005 "変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(
\r
1006 w1=self._width, h1=self._height,
\r
1007 w2=self._width+dw, h2=self._height+dh))
\r
1009 def getsize(self):
\r
1014 if self.topbutton.isChecked():
\r
1015 top = self.topsize.value()
\r
1016 elif self.bottombutton.isChecked():
\r
1017 bottom = self.bottomsize.value()
\r
1018 if self.leftbutton.isChecked():
\r
1019 left = self.leftsize.value()
\r
1020 elif self.rightbutton.isChecked():
\r
1021 right = self.rightsize.value()
\r
1022 return (top, bottom, left, right)
\r
1025 class MapImages(object):
\r
1026 def __init__(self, show_wall_menu_string):
\r
1027 if show_wall_menu_string:
\r
1028 vtext = ["なし", "壁", "扉", "扉(→)", "扉(←)", "一通(→)", "一通(←)", "隠", "隠(→)", "隠(←)",]
\r
1029 htext = ["なし", "壁", "扉", "扉(↓)", "扉(↑)", "一通(↓)", "一通(↑)", "隠", "隠(↓)", "隠(↑)",]
\r
1031 vtext = ["", "", "", "", "", "", "", "", "", "",]
\r
1032 htext = ["", "", "", "", "", "", "", "", "", "",]
\r
1033 self.wall_images = list()
\r
1034 self.wall_icons = list()
\r
1035 self.wall_texts = list()
\r
1036 for index in range(10):
\r
1037 self.wall_images.append(dict())
\r
1038 self.wall_icons.append(dict())
\r
1039 self.wall_texts.append(dict())
\r
1040 for direction in ["v", "h"]:
\r
1041 filename = os.path.join(
\r
1044 "wall_{direction}_{index:02}.png".format(
\r
1045 direction=direction, index=index))
\r
1046 self.wall_images[index][direction] = QtGui.QImage()
\r
1047 self.wall_images[index][direction].load(filename)
\r
1048 self.wall_icons[index][direction] = QtGui.QIcon(filename)
\r
1049 self.wall_texts[index]["v"] = vtext[index]
\r
1050 self.wall_texts[index]["h"] = htext[index]
\r
1054 return self.wall_images[0]["h"].width()
\r
1058 return self.wall_images[0]["v"].height()
\r
1061 def widthoffset(self):
\r
1062 return self.wall_images[0]["v"].width()//2
\r
1065 def heightoffset(self):
\r
1066 return self.wall_images[0]["h"].height()//2
\r
1068 def wall(self, index, direction):
\r
1069 return self.wall_images[index][direction]
\r
1072 class MapEngine(object):
\r
1073 hwall = " -#WMwmHVA"
\r
1074 vwall = " |#PCpc=DG"
\r
1076 def __init__(self, width, height, signx, signy, offsetx, offsety):
\r
1077 self._width = width
\r
1078 self._height = height
\r
1079 self._signx = signx
\r
1080 self._signy = signy
\r
1081 self._offsetx = offsetx
\r
1082 self._offsety = offsety
\r
1083 self.filename = None
\r
1087 self._note = dict()
\r
1089 def initdata(self):
\r
1090 width = self.width + 1
\r
1091 height = self.height + 1
\r
1092 self._data = self.initialdata(width, height)
\r
1094 def initialdata(self, width, height):
\r
1096 for x in range(width):
\r
1098 for y in range(height):
\r
1099 dt[x].append(dict())
\r
1100 for d in ["h", "v"]:
\r
1104 def inityaml(self):
\r
1105 #yaml !python/Unicode出力抑止おまじない
\r
1106 def represent_unicode(dumper, data):
\r
1107 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
\r
1108 def construct_unicode(loader, node):
\r
1109 return str(loader.construct_scalar(node))
\r
1110 yaml.add_representer(str, represent_unicode)
\r
1111 yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)
\r
1113 def getdata(self, x, y, direction):
\r
1114 return self._data[x][y][direction]
\r
1116 def setdata(self, x, y, direction, value):
\r
1117 self._data[x][y][direction] = value
\r
1121 return self._width
\r
1125 return self._height
\r
1129 return self._signx
\r
1133 return self._signy
\r
1136 def offsetx(self):
\r
1137 return self._offsetx
\r
1140 def offsety(self):
\r
1141 return self._offsety
\r
1143 def setoffset(self, x, y):
\r
1147 def setsign(self, signx, signy):
\r
1148 self._signx = signx
\r
1149 self._signy = signy
\r
1151 def getmark(self, x, y):
\r
1152 return self.unescape(self.getnote(x, y)["mark"])
\r
1154 def getdetail(self, x, y):
\r
1155 return self.unescape(self.getnote(x, y)["detail"])
\r
1157 def getforecolor(self, x, y):
\r
1158 return self.getnote(x, y)["forecolor"]
\r
1160 def getbackcolor(self, x, y):
\r
1161 return self.getnote(x, y)["backcolor"]
\r
1163 def getnote(self, x, y):
\r
1164 return self._note.get(
\r
1165 self.coodtokey(x, y), {"mark":"", "detail":"", "forecolor":"#000000", "backcolor":""})
\r
1167 def coodtokey(self, x, y):
\r
1168 return "{x:+05d}_{y:+05d}".format(x=int(x), y=int(y))
\r
1170 def keytocood(self, key):
\r
1171 return list(map(int, key.split("_")))
\r
1173 def setmark(self, x, y, mark):
\r
1174 note = self.getnote(x, y)
\r
1175 note["mark"] = self.escape(mark)
\r
1176 self.setnote(x, y, note)
\r
1178 def setdetail(self, x, y, detail):
\r
1179 note = self.getnote(x, y)
\r
1180 note["detail"] = self.escape(detail)
\r
1181 self.setnote(x, y, note)
\r
1183 def setforecolor(self, x, y, color):
\r
1184 note = self.getnote(x, y)
\r
1185 note["forecolor"] = color
\r
1186 self.setnote(x, y, note)
\r
1188 def setbackcolor(self, x, y, color):
\r
1189 note = self.getnote(x, y)
\r
1190 note["backcolor"] = color
\r
1191 self.setnote(x, y, note)
\r
1193 def setnote(self, x, y, note):
\r
1194 self._note[self.coodtokey(x, y)] = note
\r
1196 def escape(self, s):
\r
1197 return s.replace("\\", "\\\\").replace("\n", r"\n")
\r
1199 def unescape(self, s):
\r
1200 return s.replace(r"\n", "\n").replace("\\\\", "\\")
\r
1202 def viewx(self, x):
\r
1203 return x * self.signx + self.offsetx
\r
1205 def viewy(self, y):
\r
1206 return y * self.signy + self.offsety
\r
1208 def worldx(self, x):
\r
1209 return (x - self.offsetx) / self.signx
\r
1211 def worldy(self, y):
\r
1212 return (y - self.offsety) / self.signy
\r
1214 def changesize(self, top, bottom, left, right):
\r
1215 oldoffsetx = max(-left, 0)
\r
1216 newoffsetx = max(left, 0)
\r
1217 newwidth = self.width + left + right
\r
1218 oldoffsety = max(-top, 0)
\r
1219 newoffsety = max(top, 0)
\r
1220 newheight = self.height + top + bottom
\r
1222 newdata = self.initialdata(newwidth + 1, newheight + 1)
\r
1224 for x in range(min(self._width, newwidth) + 1):
\r
1225 for y in range(min(self._height, newheight) + 1):
\r
1226 for d in ["h", "v"]:
\r
1227 newdata[x+newoffsetx][y+newoffsety][d] = self._data[x+oldoffsetx][y+oldoffsety][d]
\r
1228 newnote[self.coodtokey(x+newoffsetx, y+newoffsety)] = self.getnote(x+oldoffsetx, y+oldoffsety)
\r
1229 self._width = newwidth
\r
1230 self._height = newheight
\r
1231 self.setoffset(self.offsetx -self.signx * left, self.offsety -self.signy * top)
\r
1232 self._data = newdata
\r
1233 self._note = newnote
\r
1235 def growwall(self, x1, y1, x2, y2, eraseonly, alwaysbox):
\r
1236 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1237 offsetx, offsety = self.getoffset(x1, y1, x2, y2)
\r
1239 #delete inner walls.
\r
1240 for x in range(x1, x2+stepx, stepx):
\r
1241 for y in range(y1+stepy+offsety, y2+stepy+offsety, stepy):
\r
1242 self._data[x][y]["h"] = 0
\r
1243 for x in range(x1+stepx+offsetx, x2+stepx+offsetx, stepx):
\r
1244 for y in range(y1, y2+stepy, stepy):
\r
1245 self._data[x][y]["v"] = 0
\r
1248 #draw OUTER wall if it exists.
\r
1249 if alwaysbox or (x1 == x2 and y1 == y2):
\r
1262 for x in range(x1, x2+stepx, stepx):
\r
1263 if not (vline and x == x1):
\r
1265 if self._data[x][y1+offsety]["h"] == 0:
\r
1266 self._data[x][y1+offsety]["h"] = 1
\r
1267 if self._data[x][y2+stepy+offsety]["h"] == 0:
\r
1268 self._data[x][y2+stepy+offsety]["h"] = 1
\r
1269 for y in range(y1, y2+stepy, stepy):
\r
1270 if not (hline and y == y1):
\r
1272 if self._data[x1+offsetx][y]["v"] == 0:
\r
1273 self._data[x1+offsetx][y]["v"] = 1
\r
1274 if self._data[x2+stepx+offsetx][y]["v"] == 0:
\r
1275 self._data[x2+stepx+offsetx][y]["v"] = 1
\r
1277 def fillbackcolor(self, x1, y1, x2, y2, backcolor):
\r
1278 stepx, stepy = self.getstep(x1, y1, x2, y2)
\r
1279 for x in range(x1, x2+stepx, stepx):
\r
1280 for y in range(y1, y2+stepy, stepy):
\r
1281 self.setbackcolor(x, y, backcolor)
\r
1283 def getstep(self, x1, y1, x2, y2):
\r
1292 return (stepx, stepy)
\r
1294 def getoffset(self, x1, y1, x2, y2):
\r
1303 return (offsetx, offsety)
\r
1305 def save(self, filename):
\r
1306 dt = self.savestring()
\r
1307 with open(filename, "w", encoding='utf-8') as f:
\r
1309 self.filename = filename
\r
1311 def savestring(self):
\r
1313 data["size"] = {"x":self.width, "y":self.height}
\r
1314 data["offset"] = {"x":self.offsetx, "y":self.offsety}
\r
1315 data["sign"] = {"x":self.signx, "y":self.signy}
\r
1316 data["map"] = self.getmapstring()
\r
1320 for nk, ni in list(self._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.viewx(x), self.viewy(y))] = ni
\r
1325 return yaml.safe_dump(data, allow_unicode=True,
\r
1326 default_flow_style=False, encoding='utf-8').decode('utf-8')
\r
1328 def getmapstring(self):
\r
1331 for y in range(self.height):
\r
1333 for x in range(self.width):
\r
1335 s.append(self.hwall[self._data[x][y]["h"]])
\r
1338 m.append("".join(s))
\r
1340 for x in range(self.width):
\r
1341 s.append(self.vwall[self._data[x][y]["v"]])
\r
1343 s.append(self.vwall[self._data[self.width][y]["v"]])
\r
1345 m.append("".join(s))
\r
1348 for x in range(self.width):
\r
1350 s.append(self.hwall[self._data[x][y]["h"]])
\r
1353 m.append("".join(s))
\r
1356 def load(self, filename):
\r
1357 with open(filename, "r", encoding="utf-8") as f:
\r
1359 self.loadfromstring(st)
\r
1360 self.filename = filename
\r
1362 def loadfromstring(self, st):
\r
1363 data = yaml.safe_load(st)
\r
1366 self._width = data["size"]["x"]
\r
1367 self._height = data["size"]["y"]
\r
1368 self._signx = data["sign"]["x"]
\r
1369 self._signy = data["sign"]["y"]
\r
1370 self._offsetx = data["offset"]["x"]
\r
1371 self._offsety = data["offset"]["y"]
\r
1375 for y in range(self.height):
\r
1376 for x in range(self.width):
\r
1377 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1378 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1380 for y in range(self.height):
\r
1381 self._data[x][y]["v"] = self.vwall.find(data["map"][y*2+1][1+x*2])
\r
1383 for x in range(self.width):
\r
1384 self._data[x][y]["h"] = self.hwall.find(data["map"][y*2][1+x*2+1])
\r
1388 for nk, ni in list(data["note"].items()):
\r
1389 if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":
\r
1390 x, y = self.keytocood(nk)
\r
1391 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni
\r
1396 class UndoManager(QtCore.QObject):
\r
1397 MAX_UNDO_COUNT = 128
\r
1398 changed = QtCore.Signal(bool, bool)
\r
1400 def __init__(self):
\r
1401 super(UndoManager, self).__init__()
\r
1405 self._undo = [None for x in range(self.MAX_UNDO_COUNT)]
\r
1407 self._undocount = 0
\r
1408 self._commited = True
\r
1409 self.changed.emit(self.canundo, self.canredo)
\r
1411 def init(self, data):
\r
1414 self._commited = True
\r
1416 def save(self, obj):
\r
1417 if self._index >= self.MAX_UNDO_COUNT:
\r
1418 self._undo = self._undo[1:]
\r
1420 self._undo.append(None)
\r
1421 self._undo[self._index] = obj
\r
1423 self._undocount = 0
\r
1424 self._commited = False
\r
1425 self.changed.emit(self.canundo, self.canredo)
\r
1429 self._undocount += 1
\r
1430 self._commited = False
\r
1431 self.changed.emit(self.canundo, self.canredo)
\r
1432 return self._undo[self._index - 1]
\r
1436 self._undocount -= 1
\r
1437 self._commited = False
\r
1438 self.changed.emit(self.canundo, self.canredo)
\r
1439 return self._undo[self._index - 1]
\r
1442 self._commited = True
\r
1445 def canundo(self):
\r
1446 return (self._index > 1)
\r
1449 def canredo(self):
\r
1450 return (self._undocount > 0)
\r
1453 def commited(self):
\r
1454 return self._commited
\r
1457 class PydunColorDialog(QtWidgets.QColorDialog):
\r
1458 def __init__(self, parent, config):
\r
1459 super(PydunColorDialog, self).__init__(parent)
\r
1460 f = QtGui.QFont(defaultfontname, 9)
\r
1462 for index in range(self.customCount()):
\r
1463 self.setCustomColor(index,
\r
1464 getcolorfromstring(
\r
1465 config.get(index, "#FFFFFF")))
\r
1466 self.updateconfig()
\r
1468 def updateconfig(self):
\r
1469 self._config = dict()
\r
1470 for index in range(self.customCount()):
\r
1471 self._config[index] = getcolorstring(self.customColor(index))
\r
1474 super(PydunColorDialog, self).exec_()
\r
1475 self.updateconfig()
\r
1479 return self._config
\r
1482 class PydunAskSaveDialog(QtWidgets.QMessageBox):
\r
1483 def __init__(self, parent, filename):
\r
1484 super(PydunAskSaveDialog, self).__init__(parent)
\r
1485 f = QtGui.QFont(defaultfontname, 9)
\r
1487 self.setText("{filename} への変更を保存しますか?".format(filename=filename))
\r
1488 self.setStandardButtons(QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Discard | QtWidgets.QMessageBox.Cancel)
\r
1489 self.setDefaultButton(QtWidgets.QMessageBox.Save)
\r
1490 self.button(QtWidgets.QMessageBox.Save).setText("保存する(&S)")
\r
1491 self.button(QtWidgets.QMessageBox.Discard).setText("保存しない(&N)")
\r
1492 self.button(QtWidgets.QMessageBox.Cancel).setText("キャンセル")
\r
1495 def getcolorstring(color):
\r
1496 return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())
\r
1498 def getcolorfromstring(colorstring):
\r
1499 return QtGui.QColor.fromRgb(
\r
1500 int(colorstring[1:3], 16),
\r
1501 int(colorstring[3:5], 16),
\r
1502 int(colorstring[5:7], 16))
\r
1505 return os.path.dirname(os.path.abspath(sys.argv[0]))
\r
1507 def getlatestversion():
\r
1509 rss = urllib.request.urlopen(projectrssurl)
\r
1510 rssstring = rss.read()
\r
1511 rsstree = xml.etree.ElementTree.fromstring(rssstring)
\r
1512 item = rsstree.find("channel/item/title")
\r
1513 ver = (item.text.split(" "))[2]
\r
1516 ver = projectversion
\r
1522 app = QtWidgets.QApplication(sys.argv)
\r
1523 mainWin = MainWindow()
\r
1524 app.installEventFilter(mainWin.centralWidget().mapframe)
\r
1526 sys.exit(app.exec_())
\r
1530 global configfilename
\r
1531 configfilename = os.path.join(
\r
1535 with open(configfilename, "r") as f:
\r
1536 config = yaml.safe_load(f)
\r
1540 if __name__ == '__main__':
\r