BERATools 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. beratools/__init__.py +1 -7
  2. beratools/core/algo_centerline.py +491 -351
  3. beratools/core/algo_common.py +497 -0
  4. beratools/core/algo_cost.py +192 -0
  5. beratools/core/{dijkstra_algorithm.py → algo_dijkstra.py} +503 -460
  6. beratools/core/algo_footprint_rel.py +577 -0
  7. beratools/core/algo_line_grouping.py +944 -0
  8. beratools/core/algo_merge_lines.py +214 -0
  9. beratools/core/algo_split_with_lines.py +304 -0
  10. beratools/core/algo_tiler.py +428 -0
  11. beratools/core/algo_vertex_optimization.py +469 -0
  12. beratools/core/constants.py +52 -86
  13. beratools/core/logger.py +76 -85
  14. beratools/core/tool_base.py +196 -133
  15. beratools/gui/__init__.py +11 -15
  16. beratools/gui/{beratools.json → assets/beratools.json} +2185 -2300
  17. beratools/gui/batch_processing_dlg.py +513 -463
  18. beratools/gui/bt_data.py +481 -487
  19. beratools/gui/bt_gui_main.py +710 -691
  20. beratools/gui/main.py +26 -0
  21. beratools/gui/map_window.py +162 -146
  22. beratools/gui/tool_widgets.py +725 -493
  23. beratools/tools/Beratools_r_script.r +1120 -1120
  24. beratools/tools/Ht_metrics.py +116 -116
  25. beratools/tools/__init__.py +7 -7
  26. beratools/tools/batch_processing.py +136 -132
  27. beratools/tools/canopy_threshold_relative.py +672 -670
  28. beratools/tools/canopycostraster.py +222 -222
  29. beratools/tools/centerline.py +136 -176
  30. beratools/tools/common.py +857 -885
  31. beratools/tools/fl_regen_csf.py +428 -428
  32. beratools/tools/forest_line_attributes.py +408 -408
  33. beratools/tools/line_footprint_absolute.py +213 -363
  34. beratools/tools/line_footprint_fixed.py +436 -282
  35. beratools/tools/line_footprint_functions.py +733 -720
  36. beratools/tools/line_footprint_relative.py +73 -64
  37. beratools/tools/line_grouping.py +45 -0
  38. beratools/tools/ln_relative_metrics.py +615 -615
  39. beratools/tools/r_cal_lpi_elai.r +24 -24
  40. beratools/tools/r_generate_pd_focalraster.r +100 -100
  41. beratools/tools/r_interface.py +79 -79
  42. beratools/tools/r_point_density.r +8 -8
  43. beratools/tools/rpy_chm2trees.py +86 -86
  44. beratools/tools/rpy_dsm_chm_by.py +81 -81
  45. beratools/tools/rpy_dtm_by.py +63 -63
  46. beratools/tools/rpy_find_cellsize.py +43 -43
  47. beratools/tools/rpy_gnd_csf.py +74 -74
  48. beratools/tools/rpy_hummock_hollow.py +85 -85
  49. beratools/tools/rpy_hummock_hollow_raster.py +71 -71
  50. beratools/tools/rpy_las_info.py +51 -51
  51. beratools/tools/rpy_laz2las.py +40 -40
  52. beratools/tools/rpy_lpi_elai_lascat.py +466 -466
  53. beratools/tools/rpy_normalized_lidar_by.py +56 -56
  54. beratools/tools/rpy_percent_above_dbh.py +80 -80
  55. beratools/tools/rpy_points2trees.py +88 -88
  56. beratools/tools/rpy_vegcoverage.py +94 -94
  57. beratools/tools/tiler.py +48 -206
  58. beratools/tools/tool_template.py +69 -54
  59. beratools/tools/vertex_optimization.py +61 -620
  60. beratools/tools/zonal_threshold.py +144 -144
  61. beratools-0.2.1.dist-info/METADATA +109 -0
  62. beratools-0.2.1.dist-info/RECORD +74 -0
  63. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/WHEEL +1 -1
  64. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/licenses/LICENSE +22 -22
  65. beratools/gui/cli.py +0 -18
  66. beratools/gui/gui.json +0 -8
  67. beratools/gui_tk/ASCII Banners.txt +0 -248
  68. beratools/gui_tk/__init__.py +0 -20
  69. beratools/gui_tk/beratools_main.py +0 -515
  70. beratools/gui_tk/bt_widgets.py +0 -442
  71. beratools/gui_tk/cli.py +0 -18
  72. beratools/gui_tk/img/BERALogo.png +0 -0
  73. beratools/gui_tk/img/closed.gif +0 -0
  74. beratools/gui_tk/img/closed.png +0 -0
  75. beratools/gui_tk/img/open.gif +0 -0
  76. beratools/gui_tk/img/open.png +0 -0
  77. beratools/gui_tk/img/tool.gif +0 -0
  78. beratools/gui_tk/img/tool.png +0 -0
  79. beratools/gui_tk/main.py +0 -14
  80. beratools/gui_tk/map_window.py +0 -144
  81. beratools/gui_tk/runner.py +0 -1481
  82. beratools/gui_tk/tooltip.py +0 -55
  83. beratools/third_party/pyqtlet2/__init__.py +0 -9
  84. beratools/third_party/pyqtlet2/leaflet/__init__.py +0 -26
  85. beratools/third_party/pyqtlet2/leaflet/control/__init__.py +0 -6
  86. beratools/third_party/pyqtlet2/leaflet/control/control.py +0 -59
  87. beratools/third_party/pyqtlet2/leaflet/control/draw.py +0 -52
  88. beratools/third_party/pyqtlet2/leaflet/control/layers.py +0 -20
  89. beratools/third_party/pyqtlet2/leaflet/core/Parser.py +0 -24
  90. beratools/third_party/pyqtlet2/leaflet/core/__init__.py +0 -2
  91. beratools/third_party/pyqtlet2/leaflet/core/evented.py +0 -180
  92. beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +0 -5
  93. beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +0 -34
  94. beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +0 -1
  95. beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +0 -30
  96. beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +0 -18
  97. beratools/third_party/pyqtlet2/leaflet/layer/layer.py +0 -105
  98. beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +0 -45
  99. beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +0 -1
  100. beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +0 -91
  101. beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +0 -2
  102. beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +0 -4
  103. beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +0 -16
  104. beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +0 -5
  105. beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +0 -15
  106. beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +0 -18
  107. beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +0 -5
  108. beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +0 -14
  109. beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +0 -18
  110. beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +0 -14
  111. beratools/third_party/pyqtlet2/leaflet/map/__init__.py +0 -1
  112. beratools/third_party/pyqtlet2/leaflet/map/map.py +0 -220
  113. beratools/third_party/pyqtlet2/mapwidget.py +0 -45
  114. beratools/third_party/pyqtlet2/web/custom.js +0 -43
  115. beratools/third_party/pyqtlet2/web/map.html +0 -23
  116. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
  117. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
  118. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
  119. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
  120. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
  121. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +0 -656
  122. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +0 -6
  123. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +0 -14
  124. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +0 -4
  125. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +0 -22
  126. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +0 -43
  127. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +0 -20
  128. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
  129. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
  130. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
  131. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
  132. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
  133. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
  134. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
  135. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +0 -156
  136. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +0 -10
  137. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +0 -10
  138. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +0 -22
  139. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +0 -57
  140. beratools/tools/forest_line_ecosite.py +0 -216
  141. beratools/tools/lapis_all.py +0 -103
  142. beratools/tools/least_cost_path_from_chm.py +0 -152
  143. beratools-0.2.0.dist-info/METADATA +0 -63
  144. beratools-0.2.0.dist-info/RECORD +0 -142
  145. /beratools/gui/{img → assets}/BERALogo.png +0 -0
  146. /beratools/gui/{img → assets}/closed.gif +0 -0
  147. /beratools/gui/{img → assets}/closed.png +0 -0
  148. /beratools/{gui_tk → gui/assets}/gui.json +0 -0
  149. /beratools/gui/{img → assets}/open.gif +0 -0
  150. /beratools/gui/{img → assets}/open.png +0 -0
  151. /beratools/gui/{img → assets}/tool.gif +0 -0
  152. /beratools/gui/{img → assets}/tool.png +0 -0
  153. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/entry_points.txt +0 -0
@@ -1,463 +1,513 @@
1
- #!/usr/bin/python3
2
- # -*- coding: utf-8 -*-
3
-
4
- import pandas as pd
5
- from PyQt5.QtCore import (QDir, QItemSelectionModel, QAbstractTableModel, QModelIndex,
6
- QVariant, QSettings)
7
- from PyQt5.QtWidgets import (QTableView, QAbstractItemView, QShortcut, QDialog, QDialogButtonBox)
8
- from PyQt5.QtGui import QKeySequence, QTextDocument, QTextCursor, QTextTableFormat
9
- from PyQt5 import QtPrintSupport
10
-
11
- from beratools.gui.tool_widgets import *
12
- from bt_data import *
13
- bt = BTData()
14
-
15
-
16
- class PandasModel(QAbstractTableModel):
17
- def __init__(self, df=pd.DataFrame(), parent=None):
18
- QAbstractTableModel.__init__(self, parent=None)
19
- self._df = df
20
- self.setChanged = False
21
- self.dataChanged.connect(self.setModified)
22
-
23
- def setModified(self):
24
- self.setChanged = True
25
- print(self.setChanged)
26
-
27
- def headerData(self, section, orientation, role=Qt.DisplayRole):
28
- if role != Qt.DisplayRole:
29
- return QVariant()
30
- if orientation == Qt.Horizontal:
31
- try:
32
- return self._df.columns.tolist()[section]
33
- except (IndexError,):
34
- return QVariant()
35
- elif orientation == Qt.Vertical:
36
- try:
37
- return self._df.index.tolist()[section]
38
- except (IndexError,):
39
- return QVariant()
40
-
41
- def flags(self, index):
42
- return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
43
-
44
- def data(self, index, role=Qt.DisplayRole):
45
- if index.isValid():
46
- if role == Qt.EditRole:
47
- return self._df.values[index.row()][index.column()]
48
- elif role == Qt.DisplayRole:
49
- return self._df.values[index.row()][index.column()]
50
- return None
51
-
52
- def data_row_dict(self, row):
53
- return self._df.iloc[row].to_dict()
54
-
55
- def setData(self, index, value, role):
56
- row = self._df.index[index.row()]
57
- col = self._df.columns[index.column()]
58
- self._df.values[row][col] = value
59
- self.dataChanged.emit(index, index)
60
- return True
61
-
62
- def rowCount(self, parent=QModelIndex()):
63
- return len(self._df.index)
64
-
65
- def columnCount(self, parent=QModelIndex()):
66
- return len(self._df.columns)
67
-
68
- def sort(self, column, order):
69
- colname = self._df.columns.tolist()[column]
70
- self.layoutAboutToBeChanged.emit()
71
- self._df.sort_values(colname, ascending=order == Qt.AscendingOrder, inplace=True)
72
- self._df.reset_index(inplace=True, drop=True)
73
- self.layoutChanged.emit()
74
-
75
- def insertRows(self, position, rows=1, index=QModelIndex()):
76
- print("\n\t\t ...insertRows() Starting position: '%s'" % position, 'with the total rows to be inserted: ', rows)
77
- self.beginInsertRows(QModelIndex(), position, position + rows - 1)
78
- # del self._data[position]
79
- default_row = []
80
- for i in range(rows):
81
- self._df.loc[len(self._df)] = self.default_record
82
-
83
- self.endInsertRows()
84
- return True
85
-
86
- def removeRows(self, position, rows=1, index=QModelIndex()):
87
- print("\n\t\t ...removeRows() Starting position: '%s'" % position, 'with the total rows to be removed: ', rows)
88
- self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
89
- for i in range(rows):
90
- self._df.drop(self._df.index[position + i], inplace=True)
91
- print('removed: {}'.format(position + i))
92
-
93
- self.endRemoveRows()
94
- return True
95
-
96
- def updateRow(self, row, row_data):
97
- self._df.loc[row] = row_data
98
- self.dataChanged(row, 0)
99
-
100
- def save_csv(self, csv_file):
101
- self._df.to_csv(csv_file, index=False)
102
-
103
-
104
- class BPDialog(QDialog):
105
- # signals
106
- signal_update_tool_widgets = pyqtSignal(int)
107
-
108
- def __init__(self, tool_name, parent=None):
109
- super(BPDialog, self).__init__(parent)
110
- self.setWindowTitle('Batch Processing')
111
- self.MaxRecentFiles = 5
112
- self.window_list = []
113
- self.recent_files = []
114
- self.settings = QSettings('Richard Zeng', 'Batch Processing')
115
- self.filename = ""
116
- self.setGeometry(0, 0, 800, 600)
117
-
118
- # tableview
119
- self.table_view = QTableView()
120
- self.table_view.verticalHeader().setVisible(True)
121
- self.model = PandasModel()
122
- self.table_view.setModel(self.model)
123
- self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
124
- self.table_view.setSelectionBehavior(self.table_view.SelectRows)
125
- self.table_view.setSelectionMode(self.table_view.ExtendedSelection)
126
-
127
- self.table_view.clicked.connect(self.table_view_clicked)
128
- self.table_view.verticalHeader().sectionClicked.connect(self.table_view_vertical_header_clicked)
129
- QShortcut(Qt.Key_Up, self.table_view, activated=self.table_view_key_up)
130
- QShortcut(Qt.Key_Down, self.table_view, activated=self.table_view_key_down)
131
-
132
- # create form
133
- self.tool_name = tool_name
134
- tool_args = bt.get_bera_tool_args(self.tool_name)
135
- self.tool_widgets = ToolWidgets(tool_name, tool_args, False)
136
-
137
- # self.createToolBar()
138
- hbox_widgets = QHBoxLayout()
139
- hbox_widgets.addWidget(self.table_view, 2)
140
- hbox_widgets.addWidget(self.tool_widgets, 1)
141
-
142
- # Add project, record related button box
143
- self.project_btns = QHBoxLayout()
144
- open_button = QPushButton('Open')
145
- save_button = QPushButton('Save')
146
- save_as_button = QPushButton('Save as')
147
- delete_button = QPushButton('Delete records')
148
- add_button = QPushButton('Add record')
149
- print_button = QPushButton('Print')
150
-
151
- open_button.clicked.connect(self.load_csv)
152
- save_button.setShortcut(QKeySequence.Open)
153
- save_button.clicked.connect(self.write_csv_update)
154
- save_button.setShortcut(QKeySequence.Save)
155
- save_as_button.clicked.connect(self.write_csv)
156
- save_as_button.setShortcut(QKeySequence.SaveAs)
157
- delete_button.clicked.connect(self.table_view_delete_records)
158
- delete_button.setShortcut(QKeySequence.Delete)
159
-
160
- add_button.clicked.connect(self.table_view_add_records)
161
-
162
- self.last_files = QComboBox()
163
- self.last_files.setFixedWidth(300)
164
- self.last_files.currentIndexChanged.connect(self.load_recent)
165
-
166
- self.line_find = QLineEdit()
167
- self.line_find.setPlaceholderText("find")
168
- self.line_find.setClearButtonEnabled(True)
169
- self.line_find.setFixedWidth(250)
170
- self.line_find.returnPressed.connect(self.find_in_table)
171
-
172
- print_button.clicked.connect(self.handle_preview)
173
-
174
- self.project_btns.addWidget(open_button, QDialogButtonBox.ActionRole)
175
- self.project_btns.addWidget(save_button, QDialogButtonBox.ActionRole)
176
- self.project_btns.addWidget(save_as_button, QDialogButtonBox.ActionRole)
177
- self.project_btns.addWidget(delete_button, QDialogButtonBox.ActionRole)
178
- self.project_btns.addWidget(add_button, QDialogButtonBox.ActionRole)
179
- self.project_btns.addWidget(self.line_find, QDialogButtonBox.ActionRole)
180
- self.project_btns.addWidget(print_button, QDialogButtonBox.ActionRole)
181
-
182
- # Add OK/cancel buttons
183
- self.ok_btn_box = QDialogButtonBox()
184
- self.ok_btn_box.addButton("Run", QDialogButtonBox.AcceptRole)
185
- self.ok_btn_box.addButton("Cancel", QDialogButtonBox.RejectRole)
186
- self.ok_btn_box.addButton("Help", QDialogButtonBox.HelpRole)
187
-
188
- self.ok_btn_box.accepted.connect(self.run)
189
- self.ok_btn_box.rejected.connect(self.reject)
190
- self.ok_btn_box.helpRequested.connect(self.help)
191
-
192
- hbox_btns = QHBoxLayout()
193
- hbox_btns.addLayout(self.project_btns)
194
- hbox_btns.addWidget(self.ok_btn_box)
195
-
196
- vbox_main = QVBoxLayout()
197
- vbox_main.addLayout(hbox_widgets)
198
- vbox_main.addLayout(hbox_btns)
199
- self.setLayout(vbox_main)
200
-
201
- # delete dialog when close
202
- self.setAttribute(Qt.WA_DeleteOnClose)
203
-
204
- self.setContentsMargins(10, 10, 10, 10)
205
- self.read_settings()
206
- self.table_view.setFocus()
207
- # self.statusBar().showMessage("Ready", 0)
208
-
209
- # signals
210
- self.tool_widgets.signal_save_tool_params.connect(self.table_view_update_record)
211
- self.signal_update_tool_widgets.connect(self.update_tool_widgets)
212
-
213
- def accept(self):
214
- if self.line_find.hasFocus():
215
- return
216
-
217
- print("Run the batch processing.")
218
- QDialog.accept(self)
219
-
220
- def run(self):
221
- self.model.save_csv(self.filename)
222
- self.accept()
223
-
224
- def reject(self):
225
- print("Batch processing canceled.")
226
- self.close()
227
-
228
- def help(self):
229
- print("Help requested.")
230
-
231
- def table_view_clicked(self, item):
232
- print('Row, column:{}, {}'.format(item.row(), item.column()))
233
- self.signal_update_tool_widgets.emit(item.row())
234
-
235
- def table_view_vertical_header_clicked(self, item):
236
- print('Horizontal header clicked: {}'.format(item))
237
- self.signal_update_tool_widgets.emit(item)
238
-
239
- def table_view_key_up(self):
240
- current_row = self.table_view.selectionModel().selectedRows()[-1].row()
241
- if current_row >= 1:
242
- self.table_view.selectRow(current_row - 1)
243
- self.signal_update_tool_widgets.emit(current_row - 1)
244
-
245
- def table_view_delete_records(self):
246
- selected_index = self.table_view.selectionModel().selectedRows()
247
- rows = [item.row() for item in selected_index]
248
- rows.sort(reverse=True)
249
-
250
- for i in rows:
251
- self.model.removeRows(i)
252
-
253
- current_row = i
254
- if self.model.rowCount() > 0:
255
- if current_row > self.model.rowCount() - 1:
256
- current_row = self.model.rowCount() - 1
257
-
258
- self.table_view.selectRow(current_row)
259
- self.signal_update_tool_widgets.emit(current_row)
260
-
261
- print('remove row {}'.format(i))
262
-
263
- self.model.submit()
264
-
265
- def table_view_add_records(self):
266
- self.model.default_record = bt.get_bera_tool_parameters_list(self.tool_name)
267
- ret = self.model.insertRow(self.model.rowCount())
268
- if ret:
269
- count = self.model.rowCount() - 1
270
- self.table_view.selectRow(count)
271
- self.signal_update_tool_widgets.emit(count)
272
-
273
- print('Insert row in position {}'.format(count))
274
- self.model.submit()
275
-
276
- def table_view_update_record(self, row_data):
277
- current_row = self.table_view.selectionModel().selectedRows()[-1].row()
278
- self.model.updateRow(current_row, row_data)
279
-
280
- def update_tool_widgets(self, row):
281
- tool_paramas = self.model.data_row_dict(row)
282
- self.tool_widgets.update_widgets(tool_paramas)
283
- print('Update tool parameters for record {}'.format(tool_paramas))
284
-
285
- def table_view_key_down(self):
286
- current_row = self.table_view.selectionModel().selectedRows()[-1].row()
287
- if current_row < self.model.rowCount() - 1:
288
- self.table_view.selectRow(current_row + 1)
289
- self.signal_update_tool_widgets.emit(current_row + 1)
290
-
291
- def read_settings(self):
292
- print("reading settings")
293
- if self.settings.contains("geometry"):
294
- self.setGeometry(self.settings.value('geometry'))
295
- if self.settings.contains("recentFiles"):
296
- self.recent_files = self.settings.value('recentFiles')
297
- self.last_files.addItem("last Files")
298
- self.last_files.addItems(self.recent_files[:15])
299
-
300
- def save_settings(self):
301
- print("saving settings")
302
- self.settings.setValue('geometry', self.geometry())
303
- self.settings.setValue('recentFiles', self.recent_files)
304
-
305
- def closeEvent(self, event):
306
- print(self.model.setChanged)
307
- if self.model.setChanged:
308
- print("is changed, saving?")
309
- quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>"
310
- reply = QMessageBox.question(self, 'Save Confirmation',
311
- quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
312
- if reply == QMessageBox.Yes:
313
- self.write_csv_update()
314
- else:
315
- print("Settings not saved.")
316
- return
317
- else:
318
- print("nothing changed.")
319
- self.save_settings()
320
-
321
- def load_recent(self):
322
- if self.last_files.currentIndex() > 0:
323
- print(self.last_files.currentText())
324
- print(self.model.setChanged)
325
- if self.model.setChanged:
326
- print("is changed, saving?")
327
- quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>"
328
- reply = QMessageBox.question(self, 'Save Confirmation',
329
- quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
330
- if reply == QMessageBox.Yes:
331
- self.open_csv(self.last_files.currentText())
332
- else:
333
- self.open_csv(self.last_files.currentText())
334
- else:
335
- self.open_csv(self.last_files.currentText())
336
-
337
- def open_csv(self, path):
338
- f = open(path, 'r+b')
339
- with f:
340
- df = pd.read_csv(f, sep='\t|;|,|\s+', keep_default_na=False, engine='python',
341
- skipinitialspace=True, skip_blank_lines=True)
342
- f.close()
343
- self.filename = path
344
-
345
- self.model = PandasModel(df)
346
- self.table_view.setModel(self.model)
347
- self.table_view.resizeColumnsToContents()
348
- self.table_view.selectRow(0)
349
- self.signal_update_tool_widgets.emit(0)
350
- # self.statusBar().showMessage("%s %s" % (path, "loaded"), 0)
351
-
352
- def find_in_table(self):
353
- self.table_view.clearSelection()
354
- text = self.line_find.text()
355
- model = self.table_view.model()
356
- for column in range(self.model.columnCount()):
357
- start = model.index(0, column)
358
- matches = model.match(start, Qt.DisplayRole, text, -1, Qt.MatchContains)
359
- if matches:
360
- for index in matches:
361
- # print(index.row(), index.column())
362
- self.table_view.selectionModel().select(index, QItemSelectionModel.Select)
363
-
364
- def open_file(self, path=None):
365
- print(self.model.setChanged)
366
- if self.model.setChanged == True:
367
- print("is changed, saving?")
368
- quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>"
369
- reply = QMessageBox.question(self, 'Save Confirmation',
370
- quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
371
- if reply == QMessageBox.Yes:
372
- self.write_csv_update()
373
- else:
374
- print("not saved, loading ...")
375
- return
376
- path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Dokumente/CSV/",
377
- "CSV Files (*.csv)")
378
- if path:
379
- return path
380
-
381
- def load_csv(self):
382
- file_name = self.open_file()
383
- if file_name:
384
- print(file_name + " loaded")
385
- f = open(file_name, 'r+b')
386
- with f:
387
- df = pd.read_csv(f, sep='\t|;|,|\s+', keep_default_na=False, engine='python',
388
- skipinitialspace=True, skip_blank_lines=True)
389
- f.close()
390
- self.model = PandasModel(df)
391
- self.table_view.setModel(self.model)
392
- self.table_view.resizeColumnsToContents()
393
- self.table_view.selectRow(0)
394
- # self.statusBar().showMessage("%s %s" % (fileName, "loaded"), 0)
395
- self.recent_files.insert(0, file_name)
396
- self.last_files.insertItem(1, file_name)
397
-
398
- def write_csv(self):
399
- file_name, _ = QFileDialog.getSaveFileName(self, "Open File", self.filename, "CSV Files (*.csv)")
400
- if file_name:
401
- print(file_name + " saved")
402
- f = open(file_name, 'w')
403
- new_model = self.model
404
- data_frame = new_model._df.copy()
405
- data_frame.to_csv(f, sep=',', index=False, header=True, lineterminator='\n')
406
-
407
- def write_csv_update(self):
408
- if self.filename:
409
- f = open(self.filename, 'w')
410
- new_model = self.model
411
- data_frame = new_model._df.copy()
412
- data_frame.to_csv(f, sep='\t', index=False, header=False)
413
- self.model.setChanged = False
414
- print("%s %s" % (self.filename, "saved"))
415
- # self.statusBar().showMessage("%s %s" % (self.filename, "saved"), 0)
416
-
417
- def handle_preview(self):
418
- if self.model.rowCount() == 0:
419
- self.msg("no rows")
420
- else:
421
- dialog = QtPrintSupport.QPrintPreviewDialog()
422
- dialog.setFixedSize(1000, 700)
423
- dialog.paintRequested.connect(self.handle_paint_request)
424
- dialog.exec_()
425
- print("Print Preview closed")
426
-
427
- def handle_paint_request(self, printer):
428
- printer.setDocName(self.filename)
429
- document = QTextDocument()
430
- cursor = QTextCursor(document)
431
- model = self.table_view.model()
432
- table_format = QTextTableFormat()
433
- table_format.setBorder(0.2)
434
- table_format.setBorderStyle(3)
435
- table_format.setCellSpacing(0);
436
- table_format.setTopMargin(0);
437
- table_format.setCellPadding(4)
438
- table = cursor.insertTable(model.rowCount() + 1, model.columnCount(), table_format)
439
- model = self.table_view.model()
440
-
441
- # get headers
442
- myheader = []
443
- for i in range(0, model.columnCount()):
444
- myheader = model.headerData(i, Qt.Horizontal)
445
- cursor.insertText(str(myheader))
446
- cursor.movePosition(QTextCursor.NextCell)
447
- # get cells
448
- for row in range(0, model.rowCount()):
449
- for col in range(0, model.columnCount()):
450
- index = model.index(row, col)
451
- cursor.insertText(str(index.data()))
452
- cursor.movePosition(QTextCursor.NextCell)
453
- document.print_(printer)
454
-
455
-
456
- if __name__ == "__main__":
457
- app = QApplication(sys.argv)
458
- main = BPDialog('Raster Line Attributes')
459
- main.show()
460
- if len(sys.argv) > 1:
461
- main.open_csv(sys.argv[1])
462
-
463
- sys.exit(app.exec_())
1
+ """
2
+ Copyright (C) 2025 Applied Geospatial Research Group.
3
+
4
+ This script is licensed under the GNU General Public License v3.0.
5
+ See <https://gnu.org/licenses/gpl-3.0> for full license details.
6
+
7
+ Author: Richard Zeng
8
+
9
+ Description:
10
+ This script is part of the BERA Tools.
11
+ Webpage: https://github.com/appliedgrg/beratools
12
+
13
+ Batch processing dialog.
14
+ """
15
+ import sys
16
+ import pandas as pd
17
+ from PyQt5 import QtCore
18
+ from PyQt5 import QtWidgets
19
+ from PyQt5 import QtGui, QtPrintSupport
20
+
21
+ from beratools.gui.tool_widgets import ToolWidgets
22
+ from beratools.gui.bt_data import BTData
23
+ bt = BTData()
24
+
25
+
26
+ class _PandasModel(QtCore.QAbstractTableModel):
27
+ def __init__(self, df=pd.DataFrame(), parent=None):
28
+ QtCore.QAbstractTableModel.__init__(self, parent=None)
29
+ self._df = df
30
+ self.setChanged = False
31
+ self.dataChanged.connect(self.setModified)
32
+
33
+ def setModified(self):
34
+ self.setChanged = True
35
+ print(self.setChanged)
36
+
37
+ def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
38
+ if role != QtCore.Qt.DisplayRole:
39
+ return QtCore.QVariant()
40
+ if orientation == QtCore.Qt.Horizontal:
41
+ try:
42
+ return self._df.columns.tolist()[section]
43
+ except (IndexError,):
44
+ return QtCore.QVariant()
45
+ elif orientation == QtCore.Qt.Vertical:
46
+ try:
47
+ return self._df.index.tolist()[section]
48
+ except (IndexError,):
49
+ return QtCore.QVariant()
50
+
51
+ def flags(self, index):
52
+ return (
53
+ QtCore.Qt.ItemIsEnabled
54
+ | QtCore.Qt.ItemIsSelectable
55
+ | QtCore.Qt.ItemIsEditable
56
+ )
57
+
58
+ def data(self, index, role=QtCore.Qt.DisplayRole):
59
+ if index.isValid():
60
+ if role == QtCore.Qt.EditRole:
61
+ return self._df.values[index.row()][index.column()]
62
+ elif role == QtCore.Qt.DisplayRole:
63
+ return self._df.values[index.row()][index.column()]
64
+ return None
65
+
66
+ def data_row_dict(self, row):
67
+ return self._df.iloc[row].to_dict()
68
+
69
+ def setData(self, index, value, role):
70
+ row = self._df.index[index.row()]
71
+ col = self._df.columns[index.column()]
72
+ self._df.values[row][col] = value
73
+ self.dataChanged.emit(index, index)
74
+ return True
75
+
76
+ def rowCount(self, parent=QtCore.QModelIndex()):
77
+ return len(self._df.index)
78
+
79
+ def columnCount(self, parent=QtCore.QModelIndex()):
80
+ return len(self._df.columns)
81
+
82
+ def sort(self, column, order):
83
+ col_name = self._df.columns.tolist()[column]
84
+ self.layoutAboutToBeChanged.emit()
85
+ self._df.sort_values(
86
+ col_name, ascending=order == QtCore.Qt.AscendingOrder, inplace=True
87
+ )
88
+ self._df.reset_index(inplace=True, drop=True)
89
+ self.layoutChanged.emit()
90
+
91
+ def insertRows(self, position, rows=1, index=QtCore.QModelIndex()):
92
+ self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
93
+ for i in range(rows):
94
+ self._df.loc[len(self._df)] = self.default_record
95
+
96
+ self.endInsertRows()
97
+ return True
98
+
99
+ def removeRows(self, position, rows=1, index=QtCore.QModelIndex()):
100
+ self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
101
+ for i in range(rows):
102
+ self._df.drop(self._df.index[position + i], inplace=True)
103
+ print('removed: {}'.format(position + i))
104
+
105
+ self.endRemoveRows()
106
+ return True
107
+
108
+ def updateRow(self, row, row_data):
109
+ self._df.loc[row] = row_data
110
+ self.dataChanged(row, 0)
111
+
112
+ def save_csv(self, csv_file):
113
+ self._df.to_csv(csv_file, index=False)
114
+
115
+
116
+ class BPDialog(QtWidgets.QDialog):
117
+ """Batch Processing Dialog."""
118
+
119
+ # signals
120
+ signal_update_tool_widgets = QtCore.pyqtSignal(int)
121
+
122
+ def __init__(self, tool_name, parent=None):
123
+ super(BPDialog, self).__init__(parent)
124
+ self.setWindowTitle('Batch Processing')
125
+ self.MaxRecentFiles = 5
126
+ self.window_list = []
127
+ self.recent_files = []
128
+ self.settings = QtCore.QSettings('Richard Zeng', 'Batch Processing')
129
+ self.filename = ""
130
+ self.setGeometry(0, 0, 800, 600)
131
+
132
+ # table view
133
+ self.table_view = QtWidgets.QTableView()
134
+ self.table_view.verticalHeader().setVisible(True)
135
+ self.model = _PandasModel()
136
+ self.table_view.setModel(self.model)
137
+ self.table_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
138
+ self.table_view.setSelectionBehavior(self.table_view.SelectRows)
139
+ self.table_view.setSelectionMode(self.table_view.ExtendedSelection)
140
+
141
+ self.table_view.clicked.connect(self.table_view_clicked)
142
+ self.table_view.verticalHeader().sectionClicked.connect(
143
+ self.table_view_vertical_header_clicked
144
+ )
145
+ QtWidgets.QShortcut(
146
+ QtCore.Qt.Key_Up, self.table_view, activated=self.table_view_key_up
147
+ )
148
+ QtWidgets.QShortcut(
149
+ QtCore.Qt.Key_Down, self.table_view, activated=self.table_view_key_down
150
+ )
151
+
152
+ # create form
153
+ self.tool_name = tool_name
154
+ tool_args = bt.get_bera_tool_args(self.tool_name)
155
+ self.tool_widgets = ToolWidgets(tool_name, tool_args, False)
156
+
157
+ # self.createToolBar()
158
+ hbox_widgets = QtWidgets.QHBoxLayout()
159
+ hbox_widgets.addWidget(self.table_view, 2)
160
+ hbox_widgets.addWidget(self.tool_widgets, 1)
161
+
162
+ # Add project, record related button box
163
+ self.project_btns = QtWidgets.QHBoxLayout()
164
+ open_button = QtWidgets.QPushButton('Open')
165
+ save_button = QtWidgets.QPushButton('Save')
166
+ save_as_button = QtWidgets.QPushButton('Save as')
167
+ delete_button = QtWidgets.QPushButton('Delete records')
168
+ add_button = QtWidgets.QPushButton('Add record')
169
+ print_button = QtWidgets.QPushButton('Print')
170
+
171
+ open_button.clicked.connect(self.load_csv)
172
+ save_button.setShortcut(QtGui.QKeySequence.Open)
173
+ save_button.clicked.connect(self.write_csv_update)
174
+ save_button.setShortcut(QtGui.QKeySequence.Save)
175
+ save_as_button.clicked.connect(self.write_csv)
176
+ save_as_button.setShortcut(QtGui.QKeySequence.SaveAs)
177
+ delete_button.clicked.connect(self.table_view_delete_records)
178
+ delete_button.setShortcut(QtGui.QKeySequence.Delete)
179
+
180
+ add_button.clicked.connect(self.table_view_add_records)
181
+
182
+ self.last_files = QtWidgets.QComboBox()
183
+ self.last_files.setFixedWidth(300)
184
+ self.last_files.currentIndexChanged.connect(self.load_recent)
185
+
186
+ self.line_find = QtWidgets.QLineEdit()
187
+ self.line_find.setPlaceholderText("find")
188
+ self.line_find.setClearButtonEnabled(True)
189
+ self.line_find.setFixedWidth(250)
190
+ self.line_find.returnPressed.connect(self.find_in_table)
191
+
192
+ print_button.clicked.connect(self.handle_preview)
193
+
194
+ self.project_btns.addWidget(open_button, QtWidgets.QDialogButtonBox.ActionRole)
195
+ self.project_btns.addWidget(save_button, QtWidgets.QDialogButtonBox.ActionRole)
196
+ self.project_btns.addWidget(save_as_button, QtWidgets.QDialogButtonBox.ActionRole)
197
+ self.project_btns.addWidget(delete_button, QtWidgets.QDialogButtonBox.ActionRole)
198
+ self.project_btns.addWidget(add_button, QtWidgets.QDialogButtonBox.ActionRole)
199
+ self.project_btns.addWidget(self.line_find, QtWidgets.QDialogButtonBox.ActionRole)
200
+ self.project_btns.addWidget(print_button, QtWidgets.QDialogButtonBox.ActionRole)
201
+
202
+ # Add OK/cancel buttons
203
+ self.ok_btn_box = QtWidgets.QDialogButtonBox()
204
+ self.ok_btn_box.addButton("Run", QtWidgets.QDialogButtonBox.AcceptRole)
205
+ self.ok_btn_box.addButton("Cancel", QtWidgets.QDialogButtonBox.RejectRole)
206
+ self.ok_btn_box.addButton("Help", QtWidgets.QDialogButtonBox.HelpRole)
207
+
208
+ self.ok_btn_box.accepted.connect(self.run)
209
+ self.ok_btn_box.rejected.connect(self.reject)
210
+ self.ok_btn_box.helpRequested.connect(self.help)
211
+
212
+ hbox_btns = QtWidgets.QHBoxLayout()
213
+ hbox_btns.addLayout(self.project_btns)
214
+ hbox_btns.addWidget(self.ok_btn_box)
215
+
216
+ vbox_main = QtWidgets.QVBoxLayout()
217
+ vbox_main.addLayout(hbox_widgets)
218
+ vbox_main.addLayout(hbox_btns)
219
+ self.setLayout(vbox_main)
220
+
221
+ # delete dialog when close
222
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
223
+
224
+ self.setContentsMargins(10, 10, 10, 10)
225
+ self.read_settings()
226
+ self.table_view.setFocus()
227
+ # self.statusBar().showMessage("Ready", 0)
228
+
229
+ # signals
230
+ self.tool_widgets.signal_save_tool_params.connect(self.table_view_update_record)
231
+ self.signal_update_tool_widgets.connect(self.update_tool_widgets)
232
+
233
+ def accept(self):
234
+ if self.line_find.hasFocus():
235
+ return
236
+
237
+ print("Run the batch processing.")
238
+ QtWidgets.QDialog.accept(self)
239
+
240
+ def run(self):
241
+ self.model.save_csv(self.filename)
242
+ self.accept()
243
+
244
+ def reject(self):
245
+ print("Batch processing canceled.")
246
+ self.close()
247
+
248
+ def help(self):
249
+ print("Help requested.")
250
+
251
+ def table_view_clicked(self, item):
252
+ print('Row, column:{}, {}'.format(item.row(), item.column()))
253
+ self.signal_update_tool_widgets.emit(item.row())
254
+
255
+ def table_view_vertical_header_clicked(self, item):
256
+ print('Horizontal header clicked: {}'.format(item))
257
+ self.signal_update_tool_widgets.emit(item)
258
+
259
+ def table_view_key_up(self):
260
+ current_row = self.table_view.selectionModel().selectedRows()[-1].row()
261
+ if current_row >= 1:
262
+ self.table_view.selectRow(current_row - 1)
263
+ self.signal_update_tool_widgets.emit(current_row - 1)
264
+
265
+ def table_view_delete_records(self):
266
+ selected_index = self.table_view.selectionModel().selectedRows()
267
+ rows = [item.row() for item in selected_index]
268
+ rows.sort(reverse=True)
269
+
270
+ for i in rows:
271
+ self.model.removeRows(i)
272
+
273
+ current_row = i
274
+ if self.model.rowCount() > 0:
275
+ if current_row > self.model.rowCount() - 1:
276
+ current_row = self.model.rowCount() - 1
277
+
278
+ self.table_view.selectRow(current_row)
279
+ self.signal_update_tool_widgets.emit(current_row)
280
+
281
+ print('remove row {}'.format(i))
282
+
283
+ self.model.submit()
284
+
285
+ def table_view_add_records(self):
286
+ self.model.default_record = bt.get_bera_tool_parameters_list(self.tool_name)
287
+ ret = self.model.insertRow(self.model.rowCount())
288
+ if ret:
289
+ count = self.model.rowCount() - 1
290
+ self.table_view.selectRow(count)
291
+ self.signal_update_tool_widgets.emit(count)
292
+
293
+ print('Insert row in position {}'.format(count))
294
+ self.model.submit()
295
+
296
+ def table_view_update_record(self, row_data):
297
+ current_row = self.table_view.selectionModel().selectedRows()[-1].row()
298
+ self.model.updateRow(current_row, row_data)
299
+
300
+ def update_tool_widgets(self, row):
301
+ tool_params = self.model.data_row_dict(row)
302
+ self.tool_widgets.update_widgets(tool_params)
303
+ print('Update tool parameters for record {}'.format(tool_params))
304
+
305
+ def table_view_key_down(self):
306
+ current_row = self.table_view.selectionModel().selectedRows()[-1].row()
307
+ if current_row < self.model.rowCount() - 1:
308
+ self.table_view.selectRow(current_row + 1)
309
+ self.signal_update_tool_widgets.emit(current_row + 1)
310
+
311
+ def read_settings(self):
312
+ print("reading settings")
313
+ if self.settings.contains("geometry"):
314
+ self.setGeometry(self.settings.value('geometry'))
315
+ if self.settings.contains("recentFiles"):
316
+ self.recent_files = self.settings.value('recentFiles')
317
+ self.last_files.addItem("last Files")
318
+ self.last_files.addItems(self.recent_files[:15])
319
+
320
+ def save_settings(self):
321
+ print("saving settings")
322
+ self.settings.setValue('geometry', self.geometry())
323
+ self.settings.setValue('recentFiles', self.recent_files)
324
+
325
+ def closeEvent(self, event):
326
+ print(self.model.setChanged)
327
+ if self.model.setChanged:
328
+ print("is changed, saving?")
329
+ quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>"
330
+ reply = QtWidgets.QMessageBox.question(
331
+ self,
332
+ "Save Confirmation",
333
+ quit_msg,
334
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
335
+ QtWidgets.QMessageBox.Yes,
336
+ )
337
+ if reply == QtWidgets.QMessageBox.Yes:
338
+ self.write_csv_update()
339
+ else:
340
+ print("Settings not saved.")
341
+ return
342
+ else:
343
+ print("nothing changed.")
344
+ self.save_settings()
345
+
346
+ def load_recent(self):
347
+ if self.last_files.currentIndex() > 0:
348
+ print(self.last_files.currentText())
349
+ print(self.model.setChanged)
350
+ if self.model.setChanged:
351
+ print("is changed, saving?")
352
+ quit_msg = "<b>The document was changed.<br>Save the changes?</ b>"
353
+ reply = QtWidgets.QMessageBox.question(
354
+ self,
355
+ "Save Confirmation",
356
+ quit_msg,
357
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
358
+ QtWidgets.QMessageBox.Yes,
359
+ )
360
+ if reply == QtWidgets.QMessageBox.Yes:
361
+ self.open_csv(self.last_files.currentText())
362
+ else:
363
+ self.open_csv(self.last_files.currentText())
364
+ else:
365
+ self.open_csv(self.last_files.currentText())
366
+
367
+ def open_csv(self, path):
368
+ f = open(path, 'r+b')
369
+ with f:
370
+ df = pd.read_csv(f, sep='\t|;|,|\s+', keep_default_na=False, engine='python',
371
+ skipinitialspace=True, skip_blank_lines=True)
372
+ f.close()
373
+ self.filename = path
374
+
375
+ self.model = _PandasModel(df)
376
+ self.table_view.setModel(self.model)
377
+ self.table_view.resizeColumnsToContents()
378
+ self.table_view.selectRow(0)
379
+ self.signal_update_tool_widgets.emit(0)
380
+ # self.statusBar().showMessage("%s %s" % (path, "loaded"), 0)
381
+
382
+ def find_in_table(self):
383
+ self.table_view.clearSelection()
384
+ text = self.line_find.text()
385
+ model = self.table_view.model()
386
+ for column in range(self.model.columnCount()):
387
+ start = model.index(0, column)
388
+ matches = model.match(
389
+ start, QtCore.Qt.DisplayRole, text, -1, QtCore.Qt.MatchContains
390
+ )
391
+ if matches:
392
+ for index in matches:
393
+ self.table_view.selectionModel().select(
394
+ index, QtCore.QItemSelectionModel.Select
395
+ )
396
+
397
+ def open_file(self, path=None):
398
+ print(self.model.setChanged)
399
+ if self.model.setChanged:
400
+ print("is changed, saving?")
401
+ quit_msg = "<b>The document was changed.<br>Save the changes?</ b>"
402
+ reply = QtWidgets.QMessageBox.question(
403
+ self,
404
+ "Save Confirmation",
405
+ quit_msg,
406
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
407
+ QtWidgets.QMessageBox.Yes,
408
+ )
409
+ if reply == QtWidgets.QMessageBox.Yes:
410
+ self.write_csv_update()
411
+ else:
412
+ print("not saved, loading ...")
413
+ return
414
+ path, _ = QtWidgets.QFileDialog.getOpenFileName(
415
+ self,
416
+ "Open File",
417
+ QtCore.QDir.homePath() + "/Document/CSV/",
418
+ "CSV Files (*.csv)",
419
+ )
420
+ if path:
421
+ return path
422
+
423
+ def load_csv(self):
424
+ file_name = self.open_file()
425
+ if file_name:
426
+ print(file_name + " loaded")
427
+ f = open(file_name, 'r+b')
428
+ with f:
429
+ df = pd.read_csv(
430
+ f,
431
+ sep="\t|;|,|\s+",
432
+ keep_default_na=False,
433
+ engine="python",
434
+ skipinitialspace=True,
435
+ skip_blank_lines=True,
436
+ )
437
+ f.close()
438
+ self.model = _PandasModel(df)
439
+ self.table_view.setModel(self.model)
440
+ self.table_view.resizeColumnsToContents()
441
+ self.table_view.selectRow(0)
442
+ # self.statusBar().showMessage("%s %s" % (fileName, "loaded"), 0)
443
+ self.recent_files.insert(0, file_name)
444
+ self.last_files.insertItem(1, file_name)
445
+
446
+ def write_csv(self):
447
+ file_name, _ = QtWidgets.QFileDialog.getSaveFileName(
448
+ self, "Open File", self.filename, "CSV Files (*.csv)"
449
+ )
450
+
451
+ if file_name:
452
+ print(file_name + " saved")
453
+ f = open(file_name, 'w')
454
+ new_model = self.model
455
+ data_frame = new_model._df.copy()
456
+ data_frame.to_csv(f, sep=',', index=False, header=True, lineterminator='\n')
457
+
458
+ def write_csv_update(self):
459
+ if self.filename:
460
+ f = open(self.filename, 'w')
461
+ new_model = self.model
462
+ data_frame = new_model._df.copy()
463
+ data_frame.to_csv(f, sep='\t', index=False, header=False)
464
+ self.model.setChanged = False
465
+ print("%s %s" % (self.filename, "saved"))
466
+ # self.statusBar().showMessage("%s %s" % (self.filename, "saved"), 0)
467
+
468
+ def handle_preview(self):
469
+ if self.model.rowCount() == 0:
470
+ self.msg("no rows")
471
+ else:
472
+ dialog = QtPrintSupport.QPrintPreviewDialog()
473
+ dialog.setFixedSize(1000, 700)
474
+ dialog.paintRequested.connect(self.handle_paint_request)
475
+ dialog.exec_()
476
+ print("Print Preview closed")
477
+
478
+ def handle_paint_request(self, printer):
479
+ printer.setDocName(self.filename)
480
+ document = QtGui.QTextDocument()
481
+ cursor = QtGui.QTextCursor(document)
482
+ model = self.table_view.model()
483
+ table_format = QtGui.QTextTableFormat()
484
+ table_format.setBorder(0.2)
485
+ table_format.setBorderStyle(3)
486
+ table_format.setCellSpacing(0)
487
+ table_format.setTopMargin(0)
488
+ table_format.setCellPadding(4)
489
+ model = self.table_view.model()
490
+
491
+ # get headers
492
+ my_header = []
493
+ for i in range(0, model.columnCount()):
494
+ my_header = model.headerData(i, QtCore.Qt.Horizontal)
495
+ cursor.insertText(str(my_header))
496
+ cursor.movePosition(QtGui.QTextCursor.NextCell)
497
+ # get cells
498
+ for row in range(0, model.rowCount()):
499
+ for col in range(0, model.columnCount()):
500
+ index = model.index(row, col)
501
+ cursor.insertText(str(index.data()))
502
+ cursor.movePosition(QtGui.QTextCursor.NextCell)
503
+ document.print_(printer)
504
+
505
+
506
+ if __name__ == "__main__":
507
+ app = QtCore.QApplication(sys.argv)
508
+ main = BPDialog('Raster Line Attributes')
509
+ main.show()
510
+ if len(sys.argv) > 1:
511
+ main.open_csv(sys.argv[1])
512
+
513
+ sys.exit(app.exec_())