-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathplugin_functions.py
3329 lines (2648 loc) · 133 KB
/
plugin_functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
"""
#############################################################################
## ##
## This file contains the specialized code to enable the functions of the ##
## QGIS BlueM Interface Plugin (Masterthesis Martin Großhaus Feb 2022) ##
## ##
#############################################################################
____ _____ _____ _____
/ __ \ / ____| |_ _| / ____|
| | | | | | __ | | | (___ ______
| | | | | | |_ | | | \___ \ |______|
| |__| | | |__| | _| |_ ____) |
\___\_\ \_____| |_____| |_____/
_____ _ __
|_ _| | | / _|
| | _ __ | |_ ___ _ __ | |_ __ _ ___ ___
| | | '_ \ | __| / _ \ | '__| | _| / _` | / __| / _ \
_| |_ | | | | | |_ | __/ | | | | | (_| | | (__ | __/
|_____| |_| |_| \__| \___| |_| |_| \__,_| \___| \___|
__ ____ _ __ __
/ _| | _ \ | | | \/ |
| |_ ___ _ __ | |_) | | | _ _ ___ | \ / |
| _| / _ \ | '__| | _ < | | | | | | / _ \ | |\/| |
| | | (_) | | | | |_) | | | | |_| | | __/ | | | |
|_| \___/ |_| |____/ |_| \__,_| \___| |_| |_|
#---------------------------------------------------------------------------#
| |
| Connect this file to the general code of the QGIS PluginBuilder by: |
| |
| 1) Copy this into the import section of "create_bluem_input_files.py": |
| from .plugin_functions import * |
| |
| 2) Replace the code IN the function "def run(self)" (same file) with: |
| prepare_plugin(self, self.first_start) |
| |
| 3) In file "create_bluem_input_files_dialog.py": |
| double the code for class & FORM_CLASS; "...Dialog" for "...Dialog2"|
| |
#---------------------------------------------------------------------------#
/***************************************************************************
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***************************************************************************/
"""
#############################################################################
# IMPORTS
import csv # to import csv data
import numpy as np # for arrays
import os # to verify file paths, open explorer etc.
import math # for rounding up
import shutil # to copy pdf files (manual)
# for date and time
from datetime import datetime
# used to change alignment of labels in warning
# (only used in eval, therefore called "unused" by IDE)
from PyQt5.QtCore import Qt
from qgis import processing
# used for validation of text and setting type of new layer fields
from PyQt5.QtCore import QRegExp, QVariant
from PyQt5.QtGui import QRegExpValidator
# IDE (e.g. PyCharm) calls imports below unresolved, but QGIS knows them
from qgis.core import QgsMapLayer, QgsMapLayerProxyModel, QgsFeature, \
QgsSettings, QgsField, QgsVectorLayer, \
QgsEditorWidgetSetup, QgsProject, edit
# import dialog windows
from .create_bluem_input_files_dialog import CreateBlueMInputFilesDialog
from .create_bluem_input_files_dialog import CreateBlueMInputFilesDialog2
#############################################################################
# DEFINE GLOBAL VARIABLES
# globals for array and its indices
global inputfiles_overview
global index_name_short
global index_name_long
global index_comment_for_dlg2
global index_attr_count
global index_example
global index_headlines
global index_add_lines_1
global index_add_lines_2
global index_add_lines_3
global index_add_lines_4
global index_add_lines_5
global index_add_lines_6
global index_add_lines_7
global index_last_line
global index_pattern
# global lists and dictionaries
global list_inputfile_types
global list_number_attr_file_max
global list_inputfile_types_standard
global list_filetypes_not_standard
global dict_indices_file_attr_names
global dict_indices_file_attr_types
global list_filetypes_for_export
# for current filetype in second window
global current_filetype_second_window
global current_list_file_attr_names
global current_list_number_attr_file
global current_number_attr_layer
global current_number_attr_file
# other globals
global self
global max_number_attr_file
global list_valid_chars_not_space
global val_rep_char
#############################################################################
# GENERAL FUNCTIONS - Allgemeine Funktionen (Kapitel 5.1) #
#############################################################################
# This function prepares the first window and general plugin functions
def prepare_plugin(add_self, first_start):
# set self for the following functions as a global, so that it does not
# need to be passed on all the time (easier for eval()-functions)
global self
self = add_self
# define dialog windows
self.dlg = CreateBlueMInputFilesDialog()
self.dlg2 = CreateBlueMInputFilesDialog2()
# show the dialog
self.dlg.show()
# set info about plugin version in both dialog windows
version_info = "version 1.3"
self.dlg.lb_version_info.setText(version_info)
self.dlg2.lb_version_info.setText(version_info)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# EXECUTION
# import information about BlueM input file types from csv into array
with open(os.path.join(os.path.dirname(__file__), "inputfiles_overview.csv"), 'r', encoding="ANSI") as file:
inputfiles_overview_data = list(csv.reader(file, delimiter=";"))
# put all information into array
global inputfiles_overview
inputfiles_overview = np.array(inputfiles_overview_data)
# check for correct indices of information in the array
# in case the columns got mixed up
global index_name_short
index_name_short = np.where(inputfiles_overview == "name_short")[1]
global index_name_long
index_name_long = np.where(inputfiles_overview == "name_long")[1]
global index_comment_for_dlg2
index_comment_for_dlg2 = \
np.where(inputfiles_overview == "comment_for_dlg2")[1]
global index_attr_count
index_attr_count = np.where(inputfiles_overview == "attr_count")[1]
global index_example
index_example = np.where(inputfiles_overview == "example")[1]
global index_headlines
index_headlines = np.where(inputfiles_overview == "headlines")[1]
global index_add_lines_1
index_add_lines_1 = np.where(inputfiles_overview == "add_lines_1")[1]
global index_add_lines_2
index_add_lines_2 = np.where(inputfiles_overview == "add_lines_2")[1]
global index_add_lines_3
index_add_lines_3 = np.where(inputfiles_overview == "add_lines_3")[1]
global index_add_lines_4
index_add_lines_4 = np.where(inputfiles_overview == "add_lines_4")[1]
global index_add_lines_5
index_add_lines_5 = np.where(inputfiles_overview == "add_lines_5")[1]
global index_add_lines_6
index_add_lines_6 = np.where(inputfiles_overview == "add_lines_6")[1]
global index_add_lines_7
index_add_lines_7 = np.where(inputfiles_overview == "add_lines_7")[1]
global index_last_line
index_last_line = np.where(inputfiles_overview == "last_line")[1]
global index_pattern
index_pattern = np.where(inputfiles_overview == "pattern")[1]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# get a global list of input file types
global list_inputfile_types
list_inputfile_types = []
for row in inputfiles_overview:
# only in this column
list_inputfile_types.append(row[int(index_name_short)])
# remove header
list_inputfile_types.remove("name_short")
# remove empty entry if necessary
while '' in list_inputfile_types:
list_inputfile_types.remove('')
# set a maximum number of attributes for all filetypes and make it a list
global max_number_attr_file
max_number_attr_file = 80 # limited by number of frames in current GUI
global list_number_attr_file_max
list_number_attr_file_max = []
for i in range(1, max_number_attr_file + 1):
if len(str(i)) == 1:
i = "0" + str(i)
list_number_attr_file_max.append(str(i))
# define a global dictionary of the indices of all file attributes
global dict_indices_file_attr_names
dict_indices_file_attr_names = {}
# fill a global dictionary of the indices of all file attributes
for i in list_number_attr_file_max:
dict_indices_file_attr_names[str("index_attr_" + i)] = \
np.where(inputfiles_overview == str("attr_" + i))[1]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# create global list for every filetype with its attribute names
for i in list_inputfile_types: # go through every filetype
current_list = [] # make temporary variable for filetype
# go through all attribute numbers of current filetype
for j in range(1, int(inputfiles_overview
# raise by 1 because its from list (no header)
[int(list_inputfile_types.index(i) + 1)]
[int(index_attr_count)])
# raise by 1 because of how "range" works
+ 1):
if len(str(j)) == 1: # give j a leading zero if necessary
j = "0" + str(j)
current_list.append(str(inputfiles_overview
# raise by 1 because its from list
# (with no header)
[int(list_inputfile_types.index(i) + 1)]
[int(dict_indices_file_attr_names
["index_attr_" + str(j)])]))
# create global list for every filetype from temporary variable
globals()[i.lower() + "_file_attr_list"] = current_list
# get a dictionary of the indices of all file attributes types
global dict_indices_file_attr_types
dict_indices_file_attr_types = {}
for i in list_number_attr_file_max:
dict_indices_file_attr_types[str("index_attr_" + i + "_type")] = \
np.where(inputfiles_overview == str("attr_" + i + "_type"))[1]
# create global dictionary for every filetype with its attribute types
for i in list_inputfile_types: # go through every filetype
current_dict = {} # make temporary variable for filetype
# go through all attribute numbers of current filetype
for j in range(1, int(inputfiles_overview
# raise by 1 because its from list (no header)
[int(list_inputfile_types.index(i) + 1)]
[int(index_attr_count)]
# raise by 1 because of how "range" works
) + 1):
if len(str(j)) == 1: # give j a leading zero if necessary
j = "0" + str(j)
current_dict["attr_" + str(j) + "_type"] = \
str(inputfiles_overview
# raise by 1 because its from list (no header)
[int(list_inputfile_types.index(i) + 1)]
[int(dict_indices_file_attr_types
["index_attr_" + str(j) + "_type"])])
# create global dictionary for every filetype from temporary variable
globals()[i.lower() + "_file_attr_type_dict"] = current_dict
# Control input validation of line edit in second tab of first window.
# Only allow: standard letters (no "ä" etc), numbers and "_"
validator = QRegExpValidator(QRegExp("[A-Z-a-z-0-9_]+"))
self.dlg.le_project_name.setValidator(validator)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# LAYER ADAPTION (in second tab of first dialog)
# sort layer-combobox (remove unwanted layer types)
suitable_types_layer_adaption = (QgsMapLayerProxyModel.VectorLayer
or QgsMapLayerProxyModel.NoGeometry)
self.dlg.cb_layer_adaption_selection \
.setFilters(suitable_types_layer_adaption)
# fill filetype selection combobox with empty item and all filetypes
self.dlg.cb_filetype_combobox.addItem(str(""))
self.dlg.cb_filetype_combobox.addItems(list_inputfile_types)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# GENERAL HOUSEKEEPING
# create empty list of files for export
global list_filetypes_for_export
list_filetypes_for_export = []
# set a global list of valid characters, that are not a whitespace (" ")
global list_valid_chars_not_space
list_valid_chars_not_space = \
['A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f',
'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'K', 'k', 'L', 'l',
'M', 'm', 'N', 'n', 'O', 'o', 'P', 'p', 'Q', 'q', 'R', 'r',
'S', 's', 'T', 't', 'U', 'u', 'V', 'v', 'W', 'w', 'X', 'x',
'Y', 'y', 'Z', 'z',
'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', 'ß', '_', '!', '¿', '?', '&',
'%', '$', '€', '#', '+', '-', '*', '/', '~', '§',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
# a list of filetypes that are currently not working
list_filetypes_not_working = [] # non, juhu! :-)
# disable functions for filetypes that are not working
for i in list_inputfile_types:
if i in list_filetypes_not_working:
# disable this combobox and set a warning
eval("self.dlg.cb_xxx_layerselection.setEnabled(False)"
.replace("xxx", i.lower()))
eval("self.dlg.lb_xxx_eng.setText('NOT WORKING')"
.replace("xxx", i.lower()))
eval("self.dlg.lb_xxx_eng.setAlignment(Qt.AlignRight)"
.replace("xxx", i.lower()))
# get a list of "not standard" filetypes
global list_filetypes_not_standard
list_filetypes_not_standard = [
"TAL" # has its own export function
]
# get a global list of "standard" filetypes by removing non standard
# filetypes from list of all filetypes
global list_inputfile_types_standard
list_inputfile_types_standard = list_inputfile_types.copy()
for i in list_filetypes_not_standard:
list_inputfile_types_standard.remove(i.upper())
# check the value replacement character
check_val_rep_char()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# GUI CONNECTIONS
# establish GUI connections only once due to "first_start"-variable
if first_start is True:
first_start = False # set first_start to FALSE for next round
general_gui_functions()
set_tab_order()
#############################################################################
# Creates a second window and fills it with the necessary information
# for the selected input file type
def open_second_window(filetype):
# differentiate between a and an depending on first letter
# of the file type name (Vowels must be in a tuple)
vowels = ("A", "E", "I", "O", "U", "a", "e", "i", "o", "u")
if filetype.startswith(vowels):
a_an = "an"
else:
a_an = "a"
# get filetype index from function
filetype_index = get_filetype_index(filetype)
# get the example for the filetype from the array of csv data
self.dlg2.lb_example.setText(
str(inputfiles_overview[int(filetype_index)][int(index_example)]))
# set tool tip for example
self.dlg2.lb_example.setToolTip(
"example of " + a_an + " " + filetype.upper() + "-file")
# get the selected layer
selected_layer = \
eval("self.dlg.cb_xxx_layerselection.currentLayer()"
.replace("xxx", str(filetype.lower())))
# get the name of the selected layer
selected_layer_name = \
eval("self.dlg.cb_xxx_layerselection.currentText()"
.replace("xxx", str(filetype.lower())))
# get the long name of the filetype
name_long = str(inputfiles_overview[int(filetype_index)]
[int(index_name_long)])
# get the comment for the filetype and format it
comment = str(inputfiles_overview[int(filetype_index)]
[int(index_comment_for_dlg2)])
comment = comment.replace(r"\n", "\n")
# get the number of attributes for the filetype
number_attr_filetype = int(inputfiles_overview[int(filetype_index)]
[int(index_attr_count)])
# get list of attributes of the layer
layer_attr = []
for field in selected_layer.fields():
layer_attr.append(field.name())
# get the number of attributes of the layer
number_attr_layer = len(layer_attr)
# create list of numbers of attributes for this filetype
list_number_attr_file = []
for i in range(1, number_attr_filetype+1):
# give i a leading zero if necessary
if len(str(i)) == 1:
i = "0"+str(i)
list_number_attr_file.append(str(i))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ATTRIBUTE FRAMES
# make all attribute frames visible for the moment
for i in list_number_attr_file_max:
eval("self.dlg2.frame_attr_XX.show()".replace("XX", i))
# get max number of row frames (5 attributes per row frame; round UP)
max_number_row_frames = math.ceil(max_number_attr_file / 5)
# make all attribute row frames invisible for the moment
for i in range(1, max_number_row_frames + 1):
eval("self.dlg2.frame_row_XX.hide()".replace("XX", str(i)))
# make a list of all superfluous attribute numbers
superfluous_attr_numbers = []
for i in list_number_attr_file_max:
if i not in list_number_attr_file:
superfluous_attr_numbers.append(i)
# hide all superfluous attribute frames
for i in superfluous_attr_numbers:
eval("self.dlg2.frame_attr_XX.hide()".replace("XX", i))
# get needed number of row frames (5 attributes per row frame; round UP)
needed_number_row_frames = math.ceil(len(list_number_attr_file) / 5)
# make needed attribute row frames visible
for i in range(1, needed_number_row_frames + 1):
eval("self.dlg2.frame_row_XX.show()".replace("XX", str(i)))
# clear all comboboxes before filling them new when a new version
# of the second window is opened
for i in list_number_attr_file_max:
# iterates through a list of all comboboxes (max)
eval("self.dlg2.cb_attr_XX.clear()".replace("XX", i))
# list of file attribute names for current filetype
current_file_attr_list = eval("xxx_file_attr_list"
.replace("xxx", filetype.lower()))
# fill the labels above the comboboxes with the file attribute names
for (i, j) in zip(list_number_attr_file, current_file_attr_list):
eval("self.dlg2.lb_attr_XX.setText(j)".replace("XX", i))
# fill comboboxes with layer attributes
for i in list_number_attr_file:
# empty item at the top
eval("self.dlg2.cb_attr_XX.addItem(str(""))".replace("XX", i))
# fill from list
eval("self.dlg2.cb_attr_XX.addItems(layer_attr)".replace("XX", i))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# SET TEXTS
# set header in second window for the selected filetype
self.dlg2.lb_header.setText(
"Create " + a_an + " " + filetype.upper() + "-file manually")
# set long name of filetype in second window
self.dlg2.lb_name_long.setText(
'aka "' + name_long + '" ')
# set info_1 in second window for the selected filetype
self.dlg2.lb_info_1.setText(
"You can create a BlueM " + filetype.upper() +
"-file that looks like\nthe example on the left with the "
"data from your\n" + '"' + selected_layer_name + '"' + "-layer.")
# set info_2 in second window for the selected filetype
self.dlg2.lb_info_2.setText(
"Just match the attributes you want (up to "
+ str(number_attr_filetype) + ")\n for the "
+ filetype.upper() + "-file with one of the "
+ str(number_attr_layer)
+ " field names\nof the layer (in the dropdown menus).")
# set title for comment box
self.dlg2.gb_comment.setTitle(
" Comment for " + filetype.upper() + "-file:")
# set comment
self.dlg2.lb_comment.setText(comment)
# hide comment box if comment is empty, and show if not empty
if comment == "":
self.dlg2.gb_comment.hide()
else:
self.dlg2.gb_comment.show()
# handle button for "correct field names"
self.dlg2.pb_correct_field_names.hide()
# only show button if necessary (field name 1 = "Field1")
if self.dlg2.cb_attr_01.itemText(1) == "Field1":
self.dlg2.pb_correct_field_names.show()
# check if a previous selection exists and set if available
previous_selection(filetype)
# open second window
self.dlg2.show()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# set global variables accordingly for use in other functions
# (e.g. correcting field names of faulty layer, etc.)
# set current filetype of second window as global
global current_filetype_second_window
current_filetype_second_window = filetype
# set current list of attribute numbers of file as global
global current_list_number_attr_file
current_list_number_attr_file = list_number_attr_file
# set current list of attribute names of file as global
global current_list_file_attr_names
current_list_file_attr_names = current_file_attr_list
# set number of layer attributes of second window as global
global current_number_attr_layer
current_number_attr_layer = number_attr_layer
# set number of file attributes of second window as global
global current_number_attr_file
current_number_attr_file = number_attr_filetype
#############################################################################
# Function for "Cancel"-button in second window
def reject_second_window():
# get filetype from open_second_window via global variable
filetype = current_filetype_second_window
# enables the layerselection, because "manually" is not checked
eval("self.dlg.cb_xxx_layerselection.setEnabled(True)"
.replace("xxx", str(filetype.lower())))
#############################################################################
# Function for "OK"-button in second window
def execute_second_window():
# get filetype from open_second_window via global variable
filetype = current_filetype_second_window
# checks the "manually"-button, which had opened the second window
eval("self.dlg.pb_xxx_manually.setChecked(True)"
.replace("xxx", str(filetype.lower())))
# put filename in list for export
list_filetypes_for_export.append(filetype.upper())
# disables the layerselection for this filetype so that it
# can not be changed unless the buttons are unchecked
eval("self.dlg.cb_xxx_layerselection.setEnabled(False)"
.replace("xxx", str(filetype.lower())))
# saves the selections from all attributes of the second window
# in a file specific global dictionary
exec("global xxx_attr_dict; xxx_attr_dict = {}"
.replace("xxx", filetype.lower()))
for xx in current_list_number_attr_file:
eval("xxx_attr_dict"
.replace("xxx", filetype.lower()))[str("attr_"+xx)] = \
eval("self.dlg2.cb_attr_"+xx).currentText()
#############################################################################
# SORTING FUNCTIONS - Sortier-Funktionen (Kapitel 5.2) #
#############################################################################
# Create a dictionary for this file type and match the layer fields to the
# required file attributes by their names
def create_dict_by_name(filetype):
# get filetype index from function
filetype_index = get_filetype_index(filetype)
# get the number of attributes for the filetype
number_attr_filetype = \
int(inputfiles_overview[int(filetype_index)]
[int(index_attr_count)])
# create list of numbers of attributes for this filetype
list_number_attr_file = []
for i in range(1, number_attr_filetype + 1):
if len(str(i)) == 1: # give i a leading zero if necessary
i = "0" + str(i)
list_number_attr_file.append(str(i))
# get list of file attribute names from global variable
list_names_attr_file = eval(filetype.lower()+"_file_attr_list")
# get the selected layer
selected_layer = \
eval("self.dlg.cb_xxx_layerselection.currentLayer()"
.replace("xxx", str(filetype.lower())))
# get list of layer attribute names
list_names_attr_layer = []
for field in selected_layer.fields():
list_names_attr_layer.append(field.name())
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# CREATE NEW DICT
# create current dictionary and fill it with all the necessary
# keys (with empty values) for this filetype
current_dict = {}
for number in list_number_attr_file:
current_dict["attr_" + number] = ""
# go through all attribute names of layer
for name_attr_layer in list_names_attr_layer:
# check if these names are used in the file
# (make everything temporary upper case, to ignore case)
if name_attr_layer.upper() in \
[x.upper() for x in list_names_attr_file]:
# get the index of that name in file attribute list
# (make everything temporary upper case, to ignore case)
index_attr_in_file = \
[x.upper() for x in list_names_attr_file].index(
name_attr_layer.upper())
# raise by one because the index comes from
# a list (starts at 0) but we start at "01"
index_attr_in_file += 1
# make it a string and add a leading zero if necessary
index_attr_in_file = str(index_attr_in_file)
if len(index_attr_in_file) == 1:
index_attr_in_file = "0" + index_attr_in_file
# write name of the layer attribute with the correct key in the
# current dictionary
current_dict["attr_" + index_attr_in_file] = str(name_attr_layer)
# transfer data from current dict into specific global dict
globals()[filetype.lower() + "_attr_dict"] = current_dict
#############################################################################
# fill second window with previous attribute selection if available
def previous_selection(filetype):
# check if a dict with a previous attribute selections is available
if str(filetype.lower() + "_attr_dict") in globals():
previous_dict = eval(filetype.lower() + "_attr_dict")
# for every entry in the previous dictionary
for attr_xx in previous_dict.keys():
# get index of previously selected layer attribute
prev_attr_index = eval("self.dlg2.cb_attr_xx.findText('yy')"
.replace("attr_xx", attr_xx)
.replace("yy", previous_dict[attr_xx]))
# set combobox to previous selection
eval("self.dlg2.cb_attr_xx.setCurrentIndex(yy)"
.replace("attr_xx", attr_xx)
.replace("yy", str(prev_attr_index)))
#############################################################################
# Matches attributes in second window by name if possible
def match_attributes_by_name_if_possible():
# for all attribute numbers and names of current file in second window
for xx, yy in zip(current_list_number_attr_file,
current_list_file_attr_names):
# try to find matching attribute name in every combobox
index = eval("self.dlg2.cb_attr_xx.findText('yy', "
"Qt.MatchFixedString)"
.replace("xx", xx).replace("yy", yy))
# if attribute name is not in combobox -> index is -1, therefore:
if index != -1:
# set index of combobox to the index of the found attribute name
eval("self.dlg2.cb_attr_xx.setCurrentIndex(index)"
.replace("xx", xx))
#############################################################################
# Matches attributes in order of fields of layer
def match_attributes_by_order():
# for all fields in layer for filetype in second window
for xx in range(1, current_number_attr_layer + 1):
# copy number, because one needs leading zero, the other does not
xx = str(xx)
yy = xx
# give leading zero if necessary
if len(str(xx)) < 2:
yy = str("0" + yy)
# set index of combobox to the number of layer attribute name
eval("self.dlg2.cb_attr_yy.setCurrentIndex(int(xx))"
.replace("yy", yy).replace("xx", xx))
#############################################################################
# Clears all attribute matches in second window
def clear_all_matches():
# for all attribute numbers of current file in second window
for xx in current_list_number_attr_file:
# set index of combobox to 0 i.e. first value (empty)
eval("self.dlg2.cb_attr_xx.setCurrentIndex(0)".replace("xx", xx))
#############################################################################
# GENERATE & EXPORT BLUEM-FILES #
# Erzeugen & Export von BlueM-Dateien (Kapitel 5.3) #
#############################################################################
# When the user clicks on "export"
def export_clicked():
# check how many files need to be exported
number_files_to_export = len(list_filetypes_for_export)
if number_files_to_export == 0:
# show message in message bar
self.iface.messageBar().pushMessage(
"NO FILES FOR EXPORT SELECTED",
"Please check the buttons to export BlueM files.",
duration=10)
else:
# show message in message bar
self.iface.messageBar().pushMessage(
"Exporting BlueM files: " + str(number_files_to_export) +
" - Types: " + str(list_filetypes_for_export))
# open explorer window if checked in settings (GUI)
open_explorer_window()
# investigate which buttons are checked
# and call corresponding functions
for i in list_inputfile_types_standard:
if eval("self.dlg.pb_xxx_manually.isChecked()"
.replace("xxx", i.lower())):
export_file(i)
if eval("self.dlg.pb_xxx_byname.isChecked()"
.replace("xxx", i.lower())):
export_file(i)
# check if TAL has to be exported
if self.dlg.pb_tal_manually.isChecked():
export_tal_file()
if self.dlg.pb_tal_byname.isChecked():
export_tal_file()
# export file with information about all file exports if
# checked in GUI
if self.dlg.cb_separate_file.checkState():
export_file_export_information()
# close plugin dialog after export
# only if checked in settings (GUI, dlg, tab2)
if self.dlg.cb_close_after_export.isChecked():
self.dlg.accept()
#############################################################################
# Export a file for a standard filetype
def export_file(filetype):
# get info about this file type from csv i.e. array via function
(current_layer, list_number_attr_file,
pattern, headlines, last_line,
add_lines_1, add_lines_2, add_lines_3,
add_lines_4, add_lines_5, add_lines_6,
add_lines_7) = \
get_type_info(filetype)
# get dictionaries for current file type
current_dict_name = eval(filetype.lower() + "_attr_dict")
current_dict_type = eval(filetype.lower() + "_file_attr_type_dict")
# get information about dictionaries
number_of_attr_in_file = len(current_dict_name)
number_of_matches = 0
list_attrs_not_matched = []
for attr_nr in current_dict_name.keys():
if current_dict_name[attr_nr] == "":
list_attrs_not_matched.append(attr_nr)
else:
number_of_matches += 1
# get file name from function
filename, time_of_export = construct_filename(filetype)
# create empty list for the value warnings of this file
current_file_value_warnings_list = []
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# WRITE THE FILE
# "write" file with "ANSI" encoding
with open(filename, 'w', encoding='ANSI') as file:
# write headlines in file
file.write(headlines)
feature_counter = 0
# go through every feature of layer
for feature in current_layer.getFeatures():
feature_counter += 1
# create empty list for current row
current_row = []
# go through every attribute of filetype
for xx in list_number_attr_file:
# get the required attribute data type
req_type = current_dict_type["attr_" + xx + "_type"]
# replace possible \xa0 with real spaces
req_type = req_type.replace(r'\xa0', " ")
# get required length of output
req_length = len(str(req_type))
# check if there is an entry for this attribute
if current_dict_name["attr_"+xx] == "":
# if no match in feature: fill with enough " "
current_row.append(str(" " * req_length))
else:
# handle the corresponding value
# get the feature value for attribute from layer
value = feature[current_dict_name["attr_" + xx]]
# check the value in another function
# and get potential warning
value, value_warning = \
check_value(value, req_length, req_type)
# put value as string in list for current feature
current_row.append(str(value))
# if there is a value warning
if value_warning != "":
# add column and row to value warning
value_warning = str(xx + ";" +
str(feature_counter)
+ ";" + value_warning)
# add the warning to the list for this file
current_file_value_warnings_list\
.append(value_warning)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# IMPLEMENT FILETYPE SPECIFIC FORMATS
if filetype == "FKT":
# FKT has 3 types of lines to make the table
# easier to read
# line above new "Bez"
if current_row[0][0] != " ":
file.write(add_lines_1 + "\n")
# line above new "Funktion"
if current_row[0][0] == " " \
and current_row[1][0] != " ":
file.write(add_lines_2 + "\n")
# line above new "Fkt_Nam"
if current_row[0][0] == " " \
and current_row[1][0] == " " \
and current_row[2][0] != " ":
file.write(add_lines_3 + "\n")
if filetype == "KTR":
# KTR is divided into two tables
if current_row[0][0] == "K":
# if "Bez" starts with "K" like in
# KGRP (Kontrollgruppen), insert new
# table header for it only once
try:
if kgrp_count in locals():
pass
# KGRP_count should not exist at first and even the
# check provokes an error, therefore:
except UnboundLocalError:
kgrp_count = 1
# Create KGRP_count variable, so next is "pass".
# Could have been solved differently (define
# variable earlier), but in this way all KTR-
# specific code is contained here.
file.write(add_lines_1 + "\n")
# Hopefully the table was sorted by the user,
# so that there is only KGRP in the second table.
# The table should not be sorted by python, because
# that would cause more trouble than its worth due
# to the empty values in the first column.
if filetype == "ALL" and feature_counter > 1:
pass
# only one feature / "row" is needed for an ALL-file
else:
# precede with writing row in file
# check if current row contains field names
# (maybe check every attribute, could be valid though)
# only if first attribute has a match
if current_dict_name["attr_01"] != "":
# is original first entry of first row identical
# to first field name?
if current_dict_name["attr_01"] == \
feature[current_dict_name["attr_01"]]:
# do not write row in file (it is field names)
# reduce feature counter
feature_counter -= 1
else:
# write list for current feature with
# file-specific pattern in file
file.write(pattern.format(*current_row))
else:
# write list for current feature with
# file-specific pattern in file
file.write(pattern.format(*current_row))
# write last line(s) of the table into the file
file.write(last_line)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# GET THE EXPORT INFORMATION INTO THE FILE
# construct the export information for this file
current_file_export_info = \
str("* This " + filetype.upper() + "-file was created by "
"the 'QGIS BlueM Interface Plugin'\n"
"* with data from the '" + current_layer.name() +
"'-layer\n* on the " + time_of_export + ".\n*\n" +
"* Of the " + str(number_of_attr_in_file) +
" file attributes, " + str(number_of_matches) +
" found a matching field name in the layer.\n")