ãããè¡ãã«ã¯ãè¿œå ã®ãšã¯ã¹ããŒãããã³ããŒã¿ããŒã«ã®ã³ããŒãäœæããŸãã Excel圢åŒã§ãã¡ã€ã«ã«ãšã¯ã¹ããŒãããHTML圢åŒã§ã·ã¹ãã ãããã¡ãŒã«ã³ããŒããŸãã
ããããæåã«ãããŒã«ããŒãã¡ã€ã³ãŠã£ã³ããŠã«è²Œãä»ããŸãã

ããŒã«ããŒ
ç§ãã¡ã®ã¢ããªã±ãŒã·ã§ã³ã¯ã·ã³ãã«ã§ãæ®éçã§ãæ¡åŒµå¯èœã§ããããã«èšèšãããŠããããšãæãåºãããŠãã ããã ããŒã«ããŒããŠãããŒãµã«ã§æ¡åŒµå¯èœã«ããããã«ãæ§æãã¡ã€ã«ã§ãã®å®çŸ©ãåãåºããããŒã«ããŒã¢ãžã¥ãŒã«ã«æ瀺çã«ã€ã³ããŒããããŠããªãå€éšã¢ãžã¥ãŒã«ã«é¢æ°ãé 眮ããŸãã ãããã£ãŠãæ°ãããã¿ã³ãšæ©èœã®è¿œå ã¯ãæ§æãã¡ã€ã«ã«ããããæžã蟌ãã§ãããã°ã©ã ãã£ã¬ã¯ããªã«ã¢ãžã¥ãŒã«ãè¿œå ããããšã«ãªããŸãã
Toolbar.pyãã¡ã€ã«
#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtCore import * from PyQt5.QtWidgets import * import importlib class ToolBar(QToolBar): def __init__(self, iniFile, parent=None): super(ToolBar, self).__init__(parent) ini = QSettings(iniFile, QSettings.IniFormat) ini.setIniCodec("utf-8") ini.beginGroup("Tools") for key in sorted(ini.childKeys()): v = ini.value(key) title = v[0] params = v[1:] a = self.addAction(title) a.params = params a.triggered.connect(self.execAction) ini.endGroup() def execAction(self): try: params = self.sender().params module = importlib.import_module(params[0]) if len(params) < 2: func = "run()" else: func = params[1] win = self.focusTaskWindow() exec("module.%s(win)" % func) except: print(str(sys.exc_info()[1])) return def focusTaskWindow(self): try: return QApplication.instance().focusedTaskWindow() except: return None if __name__ == '__main__': app = QApplication(sys.argv) ex = ToolBar("tools.ini") flags = Qt.Tool | Qt.WindowDoesNotAcceptFocus # | ex.windowFlags() ex.setWindowFlags(flags) ex.show() sys.exit(app.exec_())
Qtã®ããŒã«ããŒã«ã¯æ¢è£œã®QToolBarã¯ã©ã¹ããããããããç¬èªã®ToolBarãçæããŸãã ããã§ã1ã€ã®ããŒã«ããŒã§ååã§ãããããã°ã©ã ã«ããã€ãã®ããã«ãè¿œå ããå¯èœæ§ããããŸãã åããã«ã«ã¯ç¬èªã®ãã¿ã³ã»ãããæã€ç¬èªã®æ§æãã¡ã€ã«ãå¿ èŠãªã®ã§ãããŒã«ããŒãäœæãããšãã«ãã¡ã€ã«åããã©ã¡ãŒã¿ãŒãšããŠæž¡ããŸãã
æ§æãã¡ã€ã«ã¯ãäŒçµ±çã«Ini圢åŒã§UTF-8ãšã³ã³ãŒããããŸãã
class ToolBar(QToolBar): def __init__(self, iniFile, parent=None): super(ToolBar, self).__init__(parent) ini = QSettings(iniFile, QSettings.IniFormat) ini.setIniCodec("utf-8")
ãã¿ã³ãå®çŸ©ããããã®æ§æã¯ç§ãã¡ã®æã«ãããŸã;æãåçŽãªã±ãŒã¹ã§ã¯ã3ã€ã®ãã®ãå¿ èŠã§ãã
-ãã¿ã³äžã®ããã¹ã
-ãã¿ã³æ©èœãå«ãã¢ãžã¥ãŒã«
-ãã¿ã³æ©èœ
ãã¿ã³é¢æ°ã1ã€ã®ãã©ã¡ãŒã¿ãŒãã€ãŸãçŸåšã®åãŠã£ã³ããŠãåãå ¥ãããšå€æããŸãã ã¢ãžã¥ãŒã«ãããã§æ£ç¢ºã«è¡ãããšã¯ããã¿ã³ã¢ãžã¥ãŒã«ã®ã¿ã¹ã¯ã§ãããããŒã«ããŒã®ã¿ã¹ã¯ã¯ãã®åŒã³åºãã®ã¿ã«å¶éãããŸãã
次ã®tools.iniãã¡ã€ã«ãäœæããŸãã
[Tools] 001=Export to Excel,exportview,"exportToExcel" 002=Copy as HTML,exportview,"copyAsHtml"
Pythonã§ãIniãã¡ã€ã«ããå®çŸ©ã解æããŸãã
ini.beginGroup("Tools") # for key in sorted(ini.childKeys()): # list, .. ini # , v = ini.value(key) title = v[0] params = v[1:] # QAction, a = self.addAction(title) # [, ] QAction a.params = params # a.triggered.connect(self.execAction) ini.endGroup()
ãã¹ãŠã®ãã¿ã³ã«å²ãåœãŠãããå®è¡ã¡ãœããã¯ãç®çã®ã¢ãžã¥ãŒã«ãã€ã³ããŒããããããããã¿ã³ã«å²ãåœãŠãããæ©èœãåŒã³åºããŸãã ããŒã«ããŒã®ã€ã³ããŒããªã¹ãã«åã¢ãžã¥ãŒã«ãç»é²ããªãããã«ãimportlibã©ã€ãã©ãªã䜿çšããŸãã ã©ã®ãã¿ã³ãæŒãããã©ã®QActionããä¿¡å·ãéä¿¡ãããããç¥ãã ãã§ã-æšæºã¡ãœããQObject.senderïŒïŒããããæ åœããããã«æ ŒçŽããããã©ã¡ãŒã¿ãŒãååŸããã¢ãžã¥ãŒã«ã§æå³ãããŠããããšïŒãããäœã§ããïŒãå®è¡ããŸãã
def execAction(self): try: params = self.sender().params module = importlib.import_module(params[0]) func = params[1] win = self.focusTaskWindow() exec("module.%s(win)" % func) except: print(str(sys.exc_info()[1])) return
ããã«ãã¡ã€ã³ãŠã£ã³ããŠïŒtasktree.pyã¢ãžã¥ãŒã«ïŒã«è¿œå ããŸã
self.tools = ToolBar("tools.ini",self) self.addToolBar(self.tools)
éå§ããŠãããã«ã衚瀺ããããã©ããã確èªã§ããŸãã

æåã®åçã»ã©ãããã§ã¯ãªããããããŸããããäž»ãªããšã¯ãããæ©èœããããšã§ãã
ããŒã«æ©èœã¢ãžã¥ãŒã«
次ã«ããã¿ã³æ©èœãåããã¢ãžã¥ãŒã«ãäœæããŸãã ãšã¯ã¹ããŒãæ©èœãšã³ããŒæ©èœã¯1ã€ã®ããŒã¿ãœãŒã¹ã§æ©èœããåãã«ãŒã«ã«åŸã£ãŠç°ãªãã¢ãžã¥ãŒã«ã«é åžããæå³ããªããããã¢ãžã¥ãŒã«ã¯1ã€ã«ãªããŸãã
ãã¡ã€ã«exportview.py
#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys import datetime from PyQt5.QtCore import * from PyQt5.QtWidgets import * import xlsxwriter class ob(): def test(self): return 1 def exportToExcel(win): if win == None: print("No focused window") return view = focusItemView(win) title = win.windowTitle() + '.xlsx' if view == None: print("No focused item view") return # Create a workbook and add a worksheet. fileName = QFileDialog.getSaveFileName(None, 'Save Excel file', title,'Excel files (*.xlsx)') if fileName == ('',''): return indexes = view.selectionModel().selectedIndexes() if len(indexes) == 0: indexes = view.selectAll() indexes = view.selectionModel().selectedIndexes() model = view.model() d = sortedIndexes(indexes) headers = { col:model.headerData(col, Qt.Horizontal) for col in d.columns } minRow = min(d.rows) minCol = min(d.columns) try: workbook = xlsxwriter.Workbook(fileName[0]) worksheet = workbook.add_worksheet() bold = workbook.add_format({'bold': True}) dateFormat = 'dd.MM.yyyy' date = workbook.add_format({'num_format': dateFormat}) realCol = 0 for col in d.columns: worksheet.write(0, realCol, headers[col], bold) realRow = 1 for row in d.rows: if (row, col) in d.indexes: try: v = d.indexes[(row,col)].data(Qt.EditRole) if isinstance(v, QDateTime): if v.isValid() and v.toPyDateTime() > datetime.datetime(1900,1,1): v = v.toPyDateTime() worksheet.write_datetime(realRow, realCol, v, date) else: v = v.toString(dateFormat) worksheet.write(realRow, realCol, v) else: worksheet.write(realRow, realCol, v) except: print(str(sys.exc_info()[1])) realRow += 1 realCol += 1 workbook.close() except: QMessageBox.critical(None,'Export error',str(sys.exc_info()[1])) return def copyAsHtml(win): if win == None: print("No focused window") return view = focusItemView(win) if view == None: print("No focused item view") return indexes = view.selectedIndexes() if len(indexes) == 0: indexes = view.selectAll() indexes = view.selectedIndexes() if len(indexes) == 0: return; model = view.model() try: d = sortedIndexes(indexes) html = '<table><tbody>\n' headers = { col:model.headerData(col, Qt.Horizontal) for col in d.columns } html += '<tr>' for c in d.columns: html += '<th>%s</th>' % headers[c] html += '</tr>\n' for r in d.rows: html += '<tr>' for c in d.columns: if (r, c) in d.indexes: v = d.indexes[(r,c)].data(Qt.DisplayRole) html += '<td>%s</td>' % v else: html += '<td></td>' html += '</tr>' html += '</tbody></table>' mime = QMimeData() mime.setHtml(html) clipboard = QApplication.clipboard() clipboard.setMimeData(mime) except: QMessageBox.critical(None,'Export error',str(sys.exc_info()[1])) def sortedIndexes(indexes): d = ob() d.indexes = { (i.row(), i.column()):i for i in indexes } d.rows = sorted(list(set([ i[0] for i in d.indexes ]))) d.columns = sorted(list(set([ i[1] for i in d.indexes ]))) return d def headerNames(model, minCol, maxCol): headers = dict() for col in range(minCol, maxCol+1): headers[col] = model.headerData(col, Qt.Horizontal) return headers def focusItemView(win): if win == None: return None w = win.focusWidget() if w != None and isinstance(w, QTableView): return w views = win.findChildren(QTableView) if type(views) == type([]) and len(views)>0: return views[0] return None
ãããã®é¢æ°ã¯ãã¯ãšãªçµæã衚瀺ããããã«ã¢ãžã¥ãŒã«ã§äœ¿çšããQTableViewããŒã¿ããŒãã«ãšé£æºããŸãã ã¢ãžã¥ãŒã«ã®ç¬ç«æ§ãç¶æããããã«ãçŸåšã®ãŠã£ã³ããŠã§çŸåšéžæãããŠããïŒãã©ãŒã«ã¹ãããŠããïŒQTableViewã³ã³ããŒãã³ãããçŸåšã®ãŠã£ã³ããŠã®åã®äžã®ç®çã®ã¯ã©ã¹ã®æåã®ã³ã³ããŒãã³ãã®ãããããããªã³ã¶ãã©ã€ãã§æ±ºå®ããŸãã
def focusItemView(win): if win == None: return None w = win.focusWidget() if w != None and isinstance(w, QTableView): return w views = win.findChildren(QTableView) if type(views) == type([]) and len(views)>0: return views[0] return None
ããŒãã«ãããéžæããã»ã«ã®ãªã¹ããååŸããŸãã äœãéžæãããŠããªãå Žåã¯ããã¹ãŠã匷å¶çã«éžæããŸãã
indexes = view.selectionModel().selectedIndexes() if len(indexes) == 0: indexes = view.selectAll() indexes = view.selectionModel().selectedIndexes() if len(indexes) == 0: return;
Qtã§ã¯ãããŒã¿ã®é åãçŽæ¥ååŸããã®ã§ã¯ãªããã¢ãã«ã®ã€ã³ããã¯ã¹ãæäœããããšãæ¢ã«ãåãã§ãããã QModelIndexã€ã³ããã¯ã¹ã¯åçŽãªæ§é ã§ãç¹å®ã®ããŒã¿äœçœ®ïŒè¡ïŒïŒè¡ãšåïŒïŒåãããã³éå±€å ã®èŠªã€ã³ããã¯ã¹ã®èŠªïŒïŒïŒã瀺ããŸãã ã€ã³ããã¯ã¹ãåãåã£ãããdataïŒïŒã¡ãœããã䜿çšããŠã€ã³ããã¯ã¹ãååŸã§ããŸãã
ã¢ãã«å ã®éžæãããã»ã«ã®ã€ã³ããã¯ã¹ã®ãªã¹ããååŸããŸãããããã®ãªã¹ãã®ã€ã³ããã¯ã¹ã¯ããŠãŒã¶ãŒãéžæããé åºã«åŸããè¡ãšåã§ãœãŒããããŸããã ãªã¹ãã§ã¯ãªããèŸæžïŒäœçœ®âã€ã³ããã¯ã¹ïŒããã³é¢é£ããè¡ãšåã®ãœãŒãããããªã¹ãã䜿çšããæ¹ã䟿å©ã§ãã
def sortedIndexes(indexes): d = ob() # - d.indexes = { (i.row(), i.column()):i for i in indexes } d.rows = sorted(list(set([ i[0] for i in d.indexes ]))) d.columns = sorted(list(set([ i[1] for i in d.indexes ]))) return d
ã€ã³ããã¯ã¹ãªã¹ãã«ã¯ã»ãšãã©ã©ã³ãã ã«é 眮ãããã»ã«ãååšããå¯èœæ§ããããããããã©ã«ãã§ã¯QTableViewã§æªæ¥ç¶ã®ã»ã«ãéžæã§ããããšãèæ ®ããããšã䟡å€ããããŸãã

ãããã£ãŠãd.rowsã«ã¯ãã¹ãŠã®äœ¿çšæžã¿è¡ããããd.columnsã«ã¯ãã¹ãŠã®äœ¿çšæžã¿åããããŸããããããã®çµã¿åããã¯å¿ ãããd.indexesã«ãããŸããã
ããçŸããããããã«ãQTableViewã«è¡šç€ºãããååã®ãªã¹ããå¿ èŠã§ãã headerDataã¡ãœããã䜿çšããŠã¢ãã«ããããããååŸããŸãã
headers = { col:model.headerData(col, Qt.Horizontal) for col in d.columns }
ãããŸã§ããšã¯ã¹ããŒããšã³ããŒã®ã³ãŒãã¯åãã§ããããéãã¯ãªããªããŸããã
Excelã«ãšã¯ã¹ããŒã
Excelãã¡ã€ã«ã«ãšã¯ã¹ããŒãããããã«ã xlsxwriterããã±ãŒãžã䜿çšããŸãã ã éåžžã©ãããpipãä»ããŠã€ã³ã¹ããŒã«ãããŸãã
pip3 install xlsxwriter
ããã±ãŒãžã®ããã¥ã¡ã³ãã¯éåžžã«è©³çŽ°ã§ç解ãããããäŸããããããããã§ã¯è©³ãã説æããŸããã äžçªäžã®è¡ã¯ãã¬ã³ãŒããè¡ãšåã®çªå·ã§æå®ãããã»ã«ãééããããšã§ãã è¿œå ã®æžåŒèšå®ãå¿ èŠãªå Žåã¯ãã¹ã¿ã€ã«ãå®çŸ©ããã»ã«ãæžã蟌ããšãã«ã¹ã¿ã€ã«ãæå®ããå¿ èŠããããŸãã
ãŠãŒã¶ãŒã«ãšã¯ã¹ããŒãå ã®xlsxãã¡ã€ã«ã®ååãå°ããŸããQtã«ã¯ãã®ãããªæ©èœããããŸãã PyQtã§ã¯ãé¢æ°ã¯éžæããããã¡ã€ã«åãšäœ¿çšããããã£ã«ã¿ãŒã®ãªã¹ããè¿ããŸãã 空ã®è¡ã®ãªã¹ããè¿ãããå Žåãããã¯ãŠãŒã¶ãŒãéžæãæåŠããããšãæå³ããŸãã
fileName = QFileDialog.getSaveFileName(None, 'Save Excel file', title,'Excel files (*.xlsx)') if fileName == ('',''): return
å®éã«ãšã¯ã¹ããŒãïŒ
workbook = xlsxwriter.Workbook(fileName[0]) worksheet = workbook.add_worksheet() bold = workbook.add_format({'bold': True}) dateFormat = 'dd.MM.yyyy' date = workbook.add_format({'num_format': dateFormat}) realCol = 0 for col in d.columns: worksheet.write(0, realCol, headers[col], bold) realRow = 1 for row in d.rows: if (row, col) in d.indexes: try: v = d.indexes[(row,col)].data(Qt.EditRole) if isinstance(v, QDateTime): if v.isValid() and v.toPyDateTime() > datetime.datetime(1900,1,1): v = v.toPyDateTime() worksheet.write_datetime(realRow, realCol, v, date) else: v = v.toString(dateFormat) worksheet.write(realRow, realCol, v) else: worksheet.write(realRow, realCol, v) except: print(str(sys.exc_info()[1])) realRow += 1 realCol += 1 workbook.close()
PythonãQtãExcelã§æ¥ä»/æå»ã®ç解ãç°ãªããããQDateTimeã®ãã³ã¹ãè¿œå ãããŸãã-ãŸããxlsxwriterããã±ãŒãžã¯Python datetimeã§åäœããŸãããQtããQDateTimeã䜿çšããæ¹æ³ãããããªããããç¹å¥ãªé¢æ°toPyDateTimeã§è¿œå ã§å€æããå¿ èŠããããŸã; 第äºã«ãExcelã¯1900幎1æ1æ¥ã®æ¥ä»ã§ã®ã¿æ©èœããExcelã®ãã以åã®ãã¹ãŠã¯åãªãæååã§ãã
Excelãžã®ãšã¯ã¹ããŒãã®çµæïŒ

HTML圢åŒã§ã·ã¹ãã ãããã¡ã«ã³ããŒãã
ç¹ã«ããŒã¿ãå°ãªãå Žåã¯ç¹ã«ãéžæããåå¥ã®ãã¡ã€ã«ãåžžã«å¿ èŠãªããã§ã¯ãããŸãããç¹ã«ã衚圢åŒã§ã·ã¹ãã ã¯ãªããããŒãïŒã¯ãªããããŒãïŒã«ã³ããŒããExcelãWordãWebããŒãžãšãã£ã¿ãŒãªã©ã®é©åãªå Žæã«è²Œãä»ããæ¹ã䟿å©ã§ãã ã
ãããã¡ãä»ããŠè¡šåœ¢åŒããŒã¿ãã³ããŒããæãäžè¬çãªæ¹æ³ã¯ãéåžžã®HTML圢åŒã䜿çšããããšã§ãã Windowsã* nixããã³MacOSã§ã¯ããããã¡ãæäœããæ¹æ³ããŸã£ããç°ãªããŸãïŒè€æ°ããããšã¯èšããŸã§ããããŸããïŒããã®ãããQtãå®è£ ã®è©³çŽ°ãé ãã®ã¯è¯ãããšã§ãã
å¿ èŠãªã®ã¯ãQMimeDataãªããžã§ã¯ããäœæããsetHtmlã¡ãœãããä»ããŠHTMLããŒã¯ã¢ãããå ¥åããQApplicationããã¢ã¯ã»ã¹å¯èœãªã·ã¹ãã ã¯ãªããããŒãã«æž¡ãããšã§ãã
mime = QMimeData() mime.setHtml(html) clipboard = QApplication.clipboard() clipboard.setMimeData(mime)
ããããŒããå§ããŠãããŒãã«ã1è¡ãã€åéããŸãã
html = '<table><tbody>\n' headers = { col:model.headerData(col, Qt.Horizontal) for col in d.columns } html += '<tr>' for c in d.columns: html += '<th>%s</th>' % headers[c] html += '</tr>\n' for r in d.rows: html += '<tr>' for c in d.columns: if (r, c) in d.indexes: v = d.indexes[(r,c)].data(Qt.DisplayRole) html += '<td>%s</td>' % v else: html += '<td></td>' html += '</tr>' html += '</tbody></table>'
Wordã«æ¿å ¥ãããçµæïŒ

ããã§ãããŒãã«ã®å¢çç·ã¯ãWordã§ã ããã¹ãå¢çç·ã衚瀺 ããªãã·ã§ã³ãæå¹ã«ãªã£ãŠããå Žåã«ã®ã¿è¡šç€ºãããŸãããå®éã«ã¯è¡šç€ºãããŸããã ããŒãã«ãæ瀺çãªå¢çç·ã§ã³ããŒããã«ã¯ãããŒãã«ã¿ã°å ã®ããŒãã«ã®ã¹ã¿ã€ã«ãå€æŽããå¿ èŠããããŸãã ãããŸã
ãããã«
ãããã£ãŠãããŒã«ã«æ°ããé¢æ°ãè¿œå ããæ¹æ³ãããã䜿çšããããŒã¿ãœãŒã¹ãšãã®è¡šç€ºæ¹æ³ã«é¢ä¿ãªããé¢æ°ãè¿œå ãããŠåäœããŸã-ããŒã¿ãåŠçããã¢ãžã¥ãŒã«ã¯ãããŒã«ããŒãšãã®æ©èœã«ã€ããŠäœãç¥ãããããŒã«ããŒã¯æ¥ç¶ãããŠããŸããããŒã¿ã¢ãžã¥ãŒã«ããã¿ã³æ©èœã䜿çšãããããŒã«ããŒãŸãã¯ããŒã¿ã¢ãžã¥ãŒã«ã®ãããããç¥ããªããã¿ã³æ©èœã¯ãåã«çŸåšã®èŠèŠã³ã³ããŒãã³ããæ¢ç¥ã®æ¹æ³ã§åŠçããããšããŸãã
äŸã§äœ¿çšãããŠãããœãŒã¹ã¯ã以åãšåæ§ã«ãMITã©ã€ã»ã³ã¹ã®äžã§githubã«æçš¿ãããŠããŸãã
éå§-PyQtã§ããŒã«ãç ããŸã
ç¶ã-XQueryããŒã¯ã¢ããçšã®XMLã®åæ