|
@@ -89,6 +89,13 @@ else:
|
89
|
89
|
print "This script only runs under python 2"
|
90
|
90
|
exit()
|
91
|
91
|
|
|
92
|
+import platform
|
|
93
|
+current_OS = platform.system()
|
|
94
|
+
|
|
95
|
+#globals
|
|
96
|
+target_env = ''
|
|
97
|
+board_name = ''
|
|
98
|
+
|
92
|
99
|
#########
|
93
|
100
|
# Python 2 error messages:
|
94
|
101
|
# Can't find a usable init.tcl in the following directories ...
|
|
@@ -101,10 +108,6 @@ else:
|
101
|
108
|
# reboot
|
102
|
109
|
#########
|
103
|
110
|
|
104
|
|
-#globals
|
105
|
|
-target_env = ''
|
106
|
|
-board_name = ''
|
107
|
|
-
|
108
|
111
|
|
109
|
112
|
|
110
|
113
|
##########################################################################################
|
|
@@ -191,28 +194,226 @@ def get_answer(board_name, cpu_label_txt, cpu_a_txt, cpu_b_txt):
|
191
|
194
|
# end - get answer
|
192
|
195
|
|
193
|
196
|
|
|
197
|
+#
|
|
198
|
+# move custom board definitions from project folder to PlatformIO
|
|
199
|
+#
|
|
200
|
+def resolve_path(path):
|
|
201
|
+ import os
|
|
202
|
+
|
|
203
|
+ # turn the selection into a partial path
|
|
204
|
+ #get line and column numbers
|
|
205
|
+ line_num = 1
|
|
206
|
+ column_num = 1
|
|
207
|
+ line_start = path.find(':')
|
|
208
|
+ column_start = path.find(':', line_start + 1)
|
|
209
|
+ if column_start == -1:
|
|
210
|
+ column_start = len(path)
|
|
211
|
+ column_end = path.find(':', column_start + 1)
|
|
212
|
+ if column_end == -1:
|
|
213
|
+ column_end = len(path)
|
|
214
|
+ if 0 <= line_start:
|
|
215
|
+ line_num = path[ line_start + 1 : column_start]
|
|
216
|
+ if line_num == '':
|
|
217
|
+ line_num = 1
|
|
218
|
+ if not(column_start == column_end):
|
|
219
|
+ column_num = path[ column_start + 1 : column_end]
|
|
220
|
+ if column_num == '':
|
|
221
|
+ column_num = 1
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+ path = path[ : path.find(':')] # delete the line number and anything after
|
|
225
|
+ path = path.replace('\\','/')
|
|
226
|
+
|
|
227
|
+ # resolve as many '../' as we can
|
|
228
|
+ while 0 <= path.find('../'):
|
|
229
|
+ end = path.find('../') - 1
|
|
230
|
+ start = path.find('/')
|
|
231
|
+ while 0 <= path.find('/',start) and end > path.find('/',start):
|
|
232
|
+ start = path.find('/',start) + 1
|
|
233
|
+ path = path[0:start] + path[end + 4: ]
|
|
234
|
+
|
|
235
|
+ # this is an alternative to the above - it just deletes the '../' section
|
|
236
|
+ # start_temp = path.find('../')
|
|
237
|
+ # while 0 <= path.find('../',start_temp):
|
|
238
|
+ # start = path.find('../',start_temp)
|
|
239
|
+ # start_temp = start + 1
|
|
240
|
+ # if 0 <= start:
|
|
241
|
+ # path = path[start + 2 : ]
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+ start = path.find('/')
|
|
245
|
+ if not(0 == start): # make sure path starts with '/'
|
|
246
|
+ while 0 == path.find(' '): # eat any spaces at the beginning
|
|
247
|
+ path = path[ 1 : ]
|
|
248
|
+ path = '/' + path
|
|
249
|
+
|
|
250
|
+ if current_OS == 'Windows':
|
|
251
|
+ search_path = path.replace('/', '\\') # os.walk uses '\' in Windows
|
|
252
|
+ else:
|
|
253
|
+ search_path = path
|
|
254
|
+
|
|
255
|
+ start_path = os.path.abspath('')
|
|
256
|
+
|
|
257
|
+ # search project directory for the selection
|
|
258
|
+ found = False
|
|
259
|
+ full_path = ''
|
|
260
|
+ for root, directories, filenames in os.walk(start_path):
|
|
261
|
+ for filename in filenames:
|
|
262
|
+ if 0 <= root.find('.git'): # don't bother looking in this directory
|
|
263
|
+ break
|
|
264
|
+ full_path = os.path.join(root,filename)
|
|
265
|
+ if 0 <= full_path.find(search_path):
|
|
266
|
+ found = True
|
|
267
|
+ break
|
|
268
|
+ if found:
|
|
269
|
+ break
|
|
270
|
+
|
|
271
|
+ return full_path, line_num, column_num
|
|
272
|
+
|
|
273
|
+# end - resolve_path
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+#
|
|
277
|
+# Opens the file in the preferred editor at the line & column number
|
|
278
|
+# If the preferred editor isn't already running then it tries the next.
|
|
279
|
+# If none are open then the system default is used.
|
|
280
|
+#
|
|
281
|
+# Editor order:
|
|
282
|
+# 1. Notepad++ (Windows only)
|
|
283
|
+# 2. Sublime Text
|
|
284
|
+# 3. Atom
|
|
285
|
+# 4. System default (opens at line 1, column 1 only)
|
|
286
|
+#
|
|
287
|
+def open_file(path):
|
|
288
|
+ import subprocess
|
|
289
|
+ file_path, line_num, column_num = resolve_path(path)
|
|
290
|
+
|
|
291
|
+ if file_path == '' :
|
|
292
|
+ return
|
|
293
|
+
|
|
294
|
+ if current_OS == 'Windows':
|
|
295
|
+
|
|
296
|
+ editor_note = subprocess.check_output('wmic process where "name=' + "'notepad++.exe'" + '" get ExecutablePath')
|
|
297
|
+ editor_sublime = subprocess.check_output('wmic process where "name=' + "'sublime_text.exe'" + '" get ExecutablePath')
|
|
298
|
+ editor_atom = subprocess.check_output('wmic process where "name=' + "'atom.exe'" + '" get ExecutablePath')
|
|
299
|
+
|
|
300
|
+ if 0 <= editor_note.find('notepad++.exe'):
|
|
301
|
+ start = editor_note.find('\n') + 1
|
|
302
|
+ end = editor_note.find('\n',start + 5) -4
|
|
303
|
+ editor_note = editor_note[ start : end]
|
|
304
|
+ command = file_path , ' -n' + str(line_num) , ' -c' + str(column_num)
|
|
305
|
+ subprocess.Popen([editor_note, command])
|
|
306
|
+
|
|
307
|
+ elif 0 <= editor_sublime.find('sublime_text.exe'):
|
|
308
|
+ start = editor_sublime.find('\n') + 1
|
|
309
|
+ end = editor_sublime.find('\n',start + 5) -4
|
|
310
|
+ editor_sublime = editor_sublime[ start : end]
|
|
311
|
+ command = file_path + ':' + line_num + ':' + column_num
|
|
312
|
+ subprocess.Popen([editor_sublime, command])
|
|
313
|
+
|
|
314
|
+ elif 0 <= editor_atom.find('atom.exe'):
|
|
315
|
+ start = editor_atom.find('\n') + 1
|
|
316
|
+ end = editor_atom.find('\n',start + 5) -4
|
|
317
|
+ editor_atom = editor_atom[ start : end]
|
|
318
|
+ command = file_path + ':' + str(line_num) + ':' + str(column_num)
|
|
319
|
+ subprocess.Popen([editor_atom, command])
|
|
320
|
+
|
|
321
|
+ else:
|
|
322
|
+ os.startfile(resolve_path(path)) # open file with default app
|
|
323
|
+
|
|
324
|
+ elif current_OS == 'Linux':
|
|
325
|
+
|
|
326
|
+ command = file_path + ':' + str(line_num) + ':' + str(column_num)
|
|
327
|
+ running_apps = subprocess.Popen('ps ax -o cmd', stdout=subprocess.PIPE, shell=True)
|
|
328
|
+ (output, err) = running_apps.communicate()
|
|
329
|
+ temp = output.split('\n')
|
|
330
|
+
|
|
331
|
+ def find_editor_linux(name, search_obj):
|
|
332
|
+ for line in search_obj:
|
|
333
|
+ if 0 <= line.find(name):
|
|
334
|
+ path = line
|
|
335
|
+ return True, path
|
|
336
|
+ return False , ''
|
|
337
|
+
|
|
338
|
+ (success_sublime, editor_path_sublime) = find_editor_linux('sublime_text',temp)
|
|
339
|
+ (success_atom, editor_path_atom) = find_editor+linux('atom',temp)
|
|
340
|
+
|
|
341
|
+ if success_sublime:
|
|
342
|
+ subprocess.Popen([editor_path_sublime, command])
|
|
343
|
+
|
|
344
|
+ elif success_atom:
|
|
345
|
+ subprocess.Popen([editor_path_atom, command])
|
|
346
|
+
|
|
347
|
+ else:
|
|
348
|
+ os.system('xdg-open ' + file_path )
|
|
349
|
+
|
|
350
|
+ elif current_OS == 'Darwin': # MAC
|
|
351
|
+
|
|
352
|
+ command = file_path + ':' + str(line_num) + ':' + str(column_num)
|
|
353
|
+ running_apps = subprocess.Popen('ps axwww -o command', stdout=subprocess.PIPE, shell=True)
|
|
354
|
+ (output, err) = running_apps.communicate()
|
|
355
|
+ temp = output.split('\n')
|
|
356
|
+
|
|
357
|
+ def find_editor_mac(name, search_obj):
|
|
358
|
+ for line in search_obj:
|
|
359
|
+ if 0 <= line.find(name):
|
|
360
|
+ path = line
|
|
361
|
+ if 0 <= path.find('-psn'):
|
|
362
|
+ path = path[ : path.find('-psn') - 1 ]
|
|
363
|
+ return True, path
|
|
364
|
+ return False , ''
|
|
365
|
+
|
|
366
|
+ (success_sublime, editor_path_sublime) = find_editor_mac('Sublime',temp)
|
|
367
|
+ (success_atom, editor_path_atom) = find_editor_mac('Atom',temp)
|
|
368
|
+
|
|
369
|
+ if success_sublime:
|
|
370
|
+ subprocess.Popen([editor_path_sublime, command])
|
|
371
|
+
|
|
372
|
+ elif success_atom:
|
|
373
|
+ subprocess.Popen([editor_path_atom, command])
|
|
374
|
+
|
|
375
|
+ else:
|
|
376
|
+ os.system('open ' + file_path )
|
|
377
|
+# end - open_file
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+#
|
|
381
|
+# move custom board definitions from project folder to PlatformIO
|
|
382
|
+#
|
|
383
|
+def copy_boards_dir():
|
|
384
|
+
|
|
385
|
+ temp = os.environ
|
|
386
|
+ for key in temp:
|
|
387
|
+ if 0 <= os.environ[key].find('.platformio'):
|
|
388
|
+ part = os.environ[key].split(';')
|
|
389
|
+ for part2 in part:
|
|
390
|
+ if 0 <= part2.find('.platformio'):
|
|
391
|
+ path = part2
|
|
392
|
+ break
|
|
393
|
+
|
|
394
|
+ PIO_path = path[ : path.find('.platformio') + 11]
|
|
395
|
+
|
|
396
|
+# import sys
|
|
397
|
+# import subprocess
|
|
398
|
+# pio_subprocess = subprocess.Popen(['platformio', 'run', '-t', 'envdump'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
399
|
+#
|
|
400
|
+# # stream output from subprocess and split it into lines
|
|
401
|
+# for line in iter(pio_subprocess.stdout.readline, ''):
|
|
402
|
+# if 0 <= line.find('PIOHOME_DIR'):
|
|
403
|
+# start = line.find(':') + 3
|
|
404
|
+# end = line.find(',') - 1
|
|
405
|
+# PIO_path = line[start:end]
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+ PIO_path = PIO_path.replace("\\", "/")
|
|
409
|
+ PIO_path = PIO_path.replace("//", "/") + '/boards'
|
|
410
|
+
|
|
411
|
+ board_path = 'buildroot/share/PlatformIO/boards'
|
194
|
412
|
|
195
|
|
-def env_name_check(argument):
|
196
|
|
- name_check = {
|
197
|
|
- 'teensy35' : True,
|
198
|
|
- 'teensy20' : True,
|
199
|
|
- 'STM32F4' : True,
|
200
|
|
- 'STM32F1' : True,
|
201
|
|
- 'sanguino_atmega644p' : True,
|
202
|
|
- 'sanguino_atmega1284p' : True,
|
203
|
|
- 'rambo' : True,
|
204
|
|
- 'melzi_optiboot' : True,
|
205
|
|
- 'melzi' : True,
|
206
|
|
- 'megaatmega2560' : True,
|
207
|
|
- 'megaatmega1280' : True,
|
208
|
|
- 'malyanm200' : True,
|
209
|
|
- 'LPC1768' : True,
|
210
|
|
- 'DUE_debug' : True,
|
211
|
|
- 'DUE_USB' : True,
|
212
|
|
- 'DUE' : True
|
213
|
|
- }
|
|
413
|
+ from distutils.dir_util import copy_tree
|
|
414
|
+ copy_tree(board_path, PIO_path)
|
214
|
415
|
|
215
|
|
- return name_check.get(argument, False)
|
|
416
|
+# end copy_boards_dir
|
216
|
417
|
|
217
|
418
|
|
218
|
419
|
# gets the last build environment
|
|
@@ -223,14 +424,13 @@ def get_build_last():
|
223
|
424
|
date_last = 0.0
|
224
|
425
|
DIR__pioenvs = os.listdir('.pioenvs')
|
225
|
426
|
for name in DIR__pioenvs:
|
226
|
|
- if env_name_check(name):
|
227
|
|
- DIR_temp = os.listdir('.pioenvs/' + name)
|
228
|
|
- for names_temp in DIR_temp:
|
229
|
|
- if 0 == names_temp.find('firmware.'):
|
230
|
|
- date_temp = os.path.getmtime('.pioenvs/' + name + '/' + names_temp)
|
231
|
|
- if date_temp > date_last:
|
232
|
|
- date_last = date_temp
|
233
|
|
- env_last = name
|
|
427
|
+ DIR_temp = os.listdir('.pioenvs/' + name)
|
|
428
|
+ for names_temp in DIR_temp:
|
|
429
|
+ if 0 == names_temp.find('firmware.'):
|
|
430
|
+ date_temp = os.path.getmtime('.pioenvs/' + name + '/' + names_temp)
|
|
431
|
+ if date_temp > date_last:
|
|
432
|
+ date_last = date_temp
|
|
433
|
+ env_last = name
|
234
|
434
|
return env_last
|
235
|
435
|
|
236
|
436
|
|
|
@@ -296,6 +496,10 @@ def get_starting_env(board_name_full, version):
|
296
|
496
|
with open(path, 'r') as myfile:
|
297
|
497
|
pins_h = myfile.read()
|
298
|
498
|
|
|
499
|
+ env_A = ''
|
|
500
|
+ env_B = ''
|
|
501
|
+ env_C = ''
|
|
502
|
+
|
299
|
503
|
board_name = board_name_full[ 6 : ] # only use the part after "BOARD_" since we're searching the pins.h file
|
300
|
504
|
pins_h = pins_h.split('\n')
|
301
|
505
|
environment = ''
|
|
@@ -350,10 +554,10 @@ def get_env(board_name, ver_Marlin):
|
350
|
554
|
raise SystemExit(0) # quit if unable to find board
|
351
|
555
|
|
352
|
556
|
|
353
|
|
- CPU_question = ( ('1280', '2560', "1280 or 2560 CPU?"), ('644', '1284', "644 or 1284 CPU?") )
|
|
557
|
+ CPU_question = ( ('1280', '2560', " 1280 or 2560 CPU? "), ('644', '1284', " 644 or 1284 CPU? ") )
|
354
|
558
|
|
355
|
559
|
if 0 < board_name.find('MELZI') :
|
356
|
|
- get_answer(board_name, "Which flavor of Melzi?", "Melzi (Optiboot bootloader)", "Melzi ")
|
|
560
|
+ get_answer(' ' + board_name + ' ', " Which flavor of Melzi? ", "Melzi (Optiboot bootloader)", "Melzi ")
|
357
|
561
|
if 1 == get_answer_val:
|
358
|
562
|
target_env = 'melzi_optiboot'
|
359
|
563
|
else:
|
|
@@ -371,7 +575,7 @@ def get_env(board_name, ver_Marlin):
|
371
|
575
|
|
372
|
576
|
for item in CPU_question:
|
373
|
577
|
if CPU_A == item[0]:
|
374
|
|
- get_answer(board_name, item[2], item[0], item[1])
|
|
578
|
+ get_answer(' ' + board_name + ' ', item[2], item[0], item[1])
|
375
|
579
|
if 2 == get_answer_val:
|
376
|
580
|
target_env = env_B
|
377
|
581
|
else:
|
|
@@ -388,7 +592,7 @@ def get_env(board_name, ver_Marlin):
|
388
|
592
|
if build_type == 'traceback' or (build_type == 'clean' and get_build_last() == 'DUE_debug'):
|
389
|
593
|
target_env = 'DUE_debug'
|
390
|
594
|
elif env_B == 'DUE_USB':
|
391
|
|
- get_answer(board_name, "DUE: need download port", "USB (native USB) port", "Programming port ")
|
|
595
|
+ get_answer(' ' + board_name + ' ', " DUE: need download port ", "USB (native USB) port", "Programming port ")
|
392
|
596
|
if 1 == get_answer_val:
|
393
|
597
|
target_env = 'DUE_USB'
|
394
|
598
|
else:
|
|
@@ -408,6 +612,7 @@ def get_env(board_name, ver_Marlin):
|
408
|
612
|
# puts screen text into queue so that the parent thread can fetch the data from this thread
|
409
|
613
|
import Queue
|
410
|
614
|
IO_queue = Queue.Queue()
|
|
615
|
+PIO_queue = Queue.Queue()
|
411
|
616
|
def write_to_screen_queue(text, format_tag = 'normal'):
|
412
|
617
|
double_in = [text, format_tag]
|
413
|
618
|
IO_queue.put(double_in, block = False)
|
|
@@ -431,6 +636,7 @@ standard = True
|
431
|
636
|
prev_line_COM = False
|
432
|
637
|
next_line_warning = False
|
433
|
638
|
warning_continue = False
|
|
639
|
+line_counter = 0
|
434
|
640
|
|
435
|
641
|
def line_print(line_input):
|
436
|
642
|
|
|
@@ -441,6 +647,7 @@ def line_print(line_input):
|
441
|
647
|
global prev_line_COM
|
442
|
648
|
global next_line_warning
|
443
|
649
|
global warning_continue
|
|
650
|
+ global line_counter
|
444
|
651
|
|
445
|
652
|
|
446
|
653
|
|
|
@@ -490,12 +697,19 @@ def line_print(line_input):
|
490
|
697
|
write_to_screen_queue(text[found_right : ] + '\n')
|
491
|
698
|
break
|
492
|
699
|
if did_something == False:
|
493
|
|
- write_to_screen_queue(text + '\n')
|
|
700
|
+ r_loc = text.find('\r') + 1
|
|
701
|
+ if r_loc > 0 and r_loc < len(text): # need to split this line
|
|
702
|
+ text = text.split('\r')
|
|
703
|
+ for line in text:
|
|
704
|
+ write_to_screen_queue(line + '\n')
|
|
705
|
+ else:
|
|
706
|
+ write_to_screen_queue(text + '\n')
|
494
|
707
|
# end - write_to_screen_with_replace
|
495
|
708
|
|
496
|
709
|
|
497
|
710
|
|
498
|
711
|
# scan the line
|
|
712
|
+ line_counter = line_counter + 1
|
499
|
713
|
max_search = len(line_input)
|
500
|
714
|
if max_search > 3 :
|
501
|
715
|
max_search = 3
|
|
@@ -510,7 +724,14 @@ def line_print(line_input):
|
510
|
724
|
prev_line_COM = False
|
511
|
725
|
prev_line_COM = False
|
512
|
726
|
warning_continue = True
|
513
|
|
- if beginning == 'War' or \
|
|
727
|
+ if 0 < line_input.find('Thank you') or 0 < line_input.find('SUMMARY') :
|
|
728
|
+ warning = False #standard line found
|
|
729
|
+ warning_FROM = False
|
|
730
|
+ error = False
|
|
731
|
+ standard = True
|
|
732
|
+ prev_line_COM = False
|
|
733
|
+ warning_continue = False
|
|
734
|
+ elif beginning == 'War' or \
|
514
|
735
|
beginning == '#er' or \
|
515
|
736
|
beginning == 'In ' or \
|
516
|
737
|
(beginning != 'Com' and prev_line_COM == True and not(beginning == 'Arc' or beginning == 'Lin' or beginning == 'Ind') or \
|
|
@@ -539,11 +760,6 @@ def line_print(line_input):
|
539
|
760
|
error = True
|
540
|
761
|
standard = False
|
541
|
762
|
prev_line_COM = False
|
542
|
|
-
|
543
|
|
- elif beginning == 'fro' and warning == True : # start of warning /error block
|
544
|
|
- warning_FROM = True
|
545
|
|
- prev_line_COM = False
|
546
|
|
- warning_continue = True
|
547
|
763
|
elif 0 < line_input.find(': error:') or \
|
548
|
764
|
0 < line_input.find(': fatal error:'): # start of warning /error block
|
549
|
765
|
warning = False # error found
|
|
@@ -552,9 +768,14 @@ def line_print(line_input):
|
552
|
768
|
standard = False
|
553
|
769
|
prev_line_COM = False
|
554
|
770
|
warning_continue = True
|
|
771
|
+ elif beginning == 'fro' and warning == True or \
|
|
772
|
+ beginning == '.pi' : # start of warning /error block
|
|
773
|
+ warning_FROM = True
|
|
774
|
+ prev_line_COM = False
|
|
775
|
+ warning_continue = True
|
555
|
776
|
elif warning_continue == True:
|
556
|
777
|
warning = True
|
557
|
|
- warning_FROM = False # keep the warning status going until find a standard line
|
|
778
|
+ warning_FROM = False # keep the warning status going until find a standard line or an error
|
558
|
779
|
error = False
|
559
|
780
|
standard = False
|
560
|
781
|
prev_line_COM = False
|
|
@@ -608,6 +829,7 @@ def run_PIO(dummy):
|
608
|
829
|
|
609
|
830
|
import subprocess
|
610
|
831
|
import sys
|
|
832
|
+
|
611
|
833
|
print 'starting platformio'
|
612
|
834
|
|
613
|
835
|
if build_type == 'build':
|
|
@@ -664,7 +886,7 @@ def run_PIO(dummy):
|
664
|
886
|
|
665
|
887
|
# stream output from subprocess and split it into lines
|
666
|
888
|
for line in iter(pio_subprocess.stdout.readline, ''):
|
667
|
|
- line_print(line.replace('\n', ''))
|
|
889
|
+ line_print(line.replace('\n', ''))
|
668
|
890
|
|
669
|
891
|
|
670
|
892
|
# append info used to run PlatformIO
|
|
@@ -696,10 +918,16 @@ import tkFileDialog
|
696
|
918
|
|
697
|
919
|
|
698
|
920
|
class output_window(Text):
|
699
|
|
-
|
|
921
|
+ # based on Super Text
|
700
|
922
|
global continue_updates
|
701
|
923
|
continue_updates = True
|
702
|
924
|
|
|
925
|
+ global search_position
|
|
926
|
+ search_position = '' # start with invalid search position
|
|
927
|
+
|
|
928
|
+ global error_found
|
|
929
|
+ error_found = False # are there any errors?
|
|
930
|
+
|
703
|
931
|
|
704
|
932
|
def __init__(self):
|
705
|
933
|
|
|
@@ -714,6 +942,7 @@ class output_window(Text):
|
714
|
942
|
self.config(tabs=(400,)) # configure Text widget tab stops
|
715
|
943
|
self.config(background = 'black', foreground = 'white', font= ("consolas", 12), wrap = 'word', undo = 'True')
|
716
|
944
|
self.config(height = 24, width = 120)
|
|
945
|
+ self.config(insertbackground = 'pale green') # keyboard insertion point
|
717
|
946
|
self.pack(side='left', fill='both', expand=True)
|
718
|
947
|
|
719
|
948
|
self.tag_config('normal', foreground = 'white')
|
|
@@ -721,8 +950,12 @@ class output_window(Text):
|
721
|
950
|
self.tag_config('error', foreground = 'red')
|
722
|
951
|
self.tag_config('highlight_green', foreground = 'green')
|
723
|
952
|
self.tag_config('highlight_blue', foreground = 'cyan')
|
|
953
|
+ self.tag_config('error_highlight_inactive', background = 'dim gray')
|
|
954
|
+ self.tag_config('error_highlight_active', background = 'light grey')
|
724
|
955
|
|
725
|
|
-# self.bind('<Control-Key-a>', self.select_all) # the event happens but the action doesn't
|
|
956
|
+ self.bind_class("Text","<Control-a>", self.select_all) # required in windows, works in others
|
|
957
|
+ self.bind_all("<Control-Shift-E>", self.scroll_errors)
|
|
958
|
+ self.bind_class("<Control-Shift-R>", self.rebuild)
|
726
|
959
|
|
727
|
960
|
# scrollbar
|
728
|
961
|
|
|
@@ -733,15 +966,28 @@ class output_window(Text):
|
733
|
966
|
|
734
|
967
|
# pop-up menu
|
735
|
968
|
self.popup = tk.Menu(self, tearoff=0)
|
736
|
|
- self.popup.add_command(label='Cut', command=self._cut)
|
|
969
|
+
|
737
|
970
|
self.popup.add_command(label='Copy', command=self._copy)
|
738
|
971
|
self.popup.add_command(label='Paste', command=self._paste)
|
739
|
972
|
self.popup.add_separator()
|
|
973
|
+ self.popup.add_command(label='Cut', command=self._cut)
|
|
974
|
+ self.popup.add_separator()
|
740
|
975
|
self.popup.add_command(label='Select All', command=self._select_all)
|
741
|
976
|
self.popup.add_command(label='Clear All', command=self._clear_all)
|
742
|
977
|
self.popup.add_separator()
|
743
|
978
|
self.popup.add_command(label='Save As', command=self._file_save_as)
|
744
|
|
- self.bind('<Button-3>', self._show_popup)
|
|
979
|
+ self.popup.add_separator()
|
|
980
|
+ # self.popup.add_command(label='Repeat Build(CTL-shift-r)', command=self._rebuild)
|
|
981
|
+ self.popup.add_command(label='Repeat Build', command=self._rebuild)
|
|
982
|
+ self.popup.add_separator()
|
|
983
|
+ self.popup.add_command(label='Scroll Errors (CTL-shift-e)', command=self._scroll_errors)
|
|
984
|
+ self.popup.add_separator()
|
|
985
|
+ self.popup.add_command(label='Open File at Cursor', command=self._open_selected_file)
|
|
986
|
+
|
|
987
|
+ if current_OS == 'Darwin': # MAC
|
|
988
|
+ self.bind('<Button-2>', self._show_popup) # macOS only
|
|
989
|
+ else:
|
|
990
|
+ self.bind('<Button-3>', self._show_popup) # Windows & Linux
|
745
|
991
|
|
746
|
992
|
|
747
|
993
|
# threading & subprocess section
|
|
@@ -761,18 +1007,17 @@ class output_window(Text):
|
761
|
1007
|
def check_thread(self): # wait for user to kill the window
|
762
|
1008
|
global continue_updates
|
763
|
1009
|
if continue_updates == True:
|
764
|
|
- self.root.after(20, self.check_thread)
|
|
1010
|
+ self.root.after(10, self.check_thread)
|
765
|
1011
|
|
766
|
1012
|
|
767
|
1013
|
def update(self):
|
768
|
1014
|
global continue_updates
|
769
|
1015
|
if continue_updates == True:
|
770
|
|
- self.root.after(20, self.update)#method is called every 50ms
|
|
1016
|
+ self.root.after(10, self.update)#method is called every 50ms
|
771
|
1017
|
temp_text = ['0','0']
|
772
|
1018
|
if IO_queue.empty():
|
773
|
1019
|
if not(self.secondary_thread.is_alive()):
|
774
|
1020
|
continue_updates = False # queue is exhausted and thread is dead so no need for further updates
|
775
|
|
- self.tag_add('sel', '1.0', 'end')
|
776
|
1021
|
else:
|
777
|
1022
|
try:
|
778
|
1023
|
temp_text = IO_queue.get(block = False)
|
|
@@ -785,6 +1030,74 @@ class output_window(Text):
|
785
|
1030
|
|
786
|
1031
|
# text editing section
|
787
|
1032
|
|
|
1033
|
+
|
|
1034
|
+ def _scroll_errors(self):
|
|
1035
|
+ global search_position
|
|
1036
|
+ global error_found
|
|
1037
|
+ if search_position == '': # first time so highlight all errors
|
|
1038
|
+ countVar = tk.IntVar()
|
|
1039
|
+ search_position = '1.0'
|
|
1040
|
+ search_count = 0
|
|
1041
|
+ while not(search_position == '') and search_count < 100:
|
|
1042
|
+ search_position = self.search("error", search_position, stopindex="end", count=countVar, nocase=1)
|
|
1043
|
+ search_count = search_count + 1
|
|
1044
|
+ if not(search_position == ''):
|
|
1045
|
+ error_found = True
|
|
1046
|
+ end_pos = '{}+{}c'.format(search_position, 5)
|
|
1047
|
+ self.tag_add("error_highlight_inactive", search_position, end_pos)
|
|
1048
|
+ search_position = '{}+{}c'.format(search_position, 1) # point to the next character for new search
|
|
1049
|
+ else:
|
|
1050
|
+ break
|
|
1051
|
+
|
|
1052
|
+ if error_found:
|
|
1053
|
+ if search_position == '':
|
|
1054
|
+ search_position = self.search("error", '1.0', stopindex="end", nocase=1) # new search
|
|
1055
|
+ else: # remove active highlight
|
|
1056
|
+ end_pos = '{}+{}c'.format(search_position, 5)
|
|
1057
|
+ start_pos = '{}+{}c'.format(search_position, -1)
|
|
1058
|
+ self.tag_remove("error_highlight_active", start_pos, end_pos)
|
|
1059
|
+ search_position = self.search("error", search_position, stopindex="end", nocase=1) # finds first occurrence AGAIN on the first time through
|
|
1060
|
+ if search_position == "": # wrap around
|
|
1061
|
+ search_position = self.search("error", '1.0', stopindex="end", nocase=1)
|
|
1062
|
+ end_pos = '{}+{}c'.format(search_position, 5)
|
|
1063
|
+ self.tag_add("error_highlight_active", search_position, end_pos) # add active highlight
|
|
1064
|
+ self.see(search_position)
|
|
1065
|
+ search_position = '{}+{}c'.format(search_position, 1) # point to the next character for new search
|
|
1066
|
+
|
|
1067
|
+ def scroll_errors(self, event):
|
|
1068
|
+ self._scroll_errors()
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+ def _rebuild(self):
|
|
1072
|
+ #global board_name
|
|
1073
|
+ #global Marlin_ver
|
|
1074
|
+ #global target_env
|
|
1075
|
+ #board_name, Marlin_ver = get_board_name()
|
|
1076
|
+ #target_env = get_env(board_name, Marlin_ver)
|
|
1077
|
+ self.start_thread()
|
|
1078
|
+
|
|
1079
|
+ def rebuild(self, event):
|
|
1080
|
+ print "event happened"
|
|
1081
|
+ self._rebuild()
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+ def _open_selected_file(self):
|
|
1085
|
+ current_line = self.index('insert')
|
|
1086
|
+ line_start = current_line[ : current_line.find('.')] + '.0'
|
|
1087
|
+ line_end = current_line[ : current_line.find('.')] + '.200'
|
|
1088
|
+ self.mark_set("path_start", line_start)
|
|
1089
|
+ self.mark_set("path_end", line_end)
|
|
1090
|
+ path = self.get("path_start", "path_end")
|
|
1091
|
+ from_loc = path.find('from ')
|
|
1092
|
+ colon_loc = path.find(': ')
|
|
1093
|
+ if 0 <= from_loc and ((colon_loc == -1) or (from_loc < colon_loc)) :
|
|
1094
|
+ path = path [ from_loc + 5 : ]
|
|
1095
|
+ if 0 <= colon_loc:
|
|
1096
|
+ path = path [ : colon_loc ]
|
|
1097
|
+ if 0 <= path.find('\\') or 0 <= path.find('/'): # make sure it really contains a path
|
|
1098
|
+ open_file(path)
|
|
1099
|
+
|
|
1100
|
+
|
788
|
1101
|
def _file_save_as(self):
|
789
|
1102
|
self.filename = tkFileDialog.asksaveasfilename(defaultextension = '.txt')
|
790
|
1103
|
f = open(self.filename, 'w')
|
|
@@ -833,7 +1146,7 @@ class output_window(Text):
|
833
|
1146
|
pass
|
834
|
1147
|
|
835
|
1148
|
def cut(self, event):
|
836
|
|
- _cut(self)
|
|
1149
|
+ self._cut()
|
837
|
1150
|
|
838
|
1151
|
def _copy(self):
|
839
|
1152
|
|
|
@@ -845,7 +1158,7 @@ class output_window(Text):
|
845
|
1158
|
pass
|
846
|
1159
|
|
847
|
1160
|
def copy(self, event):
|
848
|
|
- _copy(self)
|
|
1161
|
+ self._copy()
|
849
|
1162
|
|
850
|
1163
|
def _paste(self):
|
851
|
1164
|
|
|
@@ -867,40 +1180,7 @@ class output_window(Text):
|
867
|
1180
|
if isok:
|
868
|
1181
|
self.delete('1.0', 'end')
|
869
|
1182
|
|
870
|
|
- def _place_cursor(self): # theme: terminal
|
871
|
|
- '''check the position of the cursor against the last known position
|
872
|
|
- every 15ms and update the cursorblock tag as needed'''
|
873
|
|
-
|
874
|
|
- current_index = self.index('insert')
|
875
|
|
-
|
876
|
|
- if self.cursor != current_index:
|
877
|
|
- self.cursor = current_index
|
878
|
|
- self.tag_delete('cursorblock')
|
879
|
|
-
|
880
|
|
- start = self.index('insert')
|
881
|
|
- end = self.index('insert+1c')
|
882
|
|
-
|
883
|
|
- if start[0] != end[0]:
|
884
|
|
- self.insert(start, ' ')
|
885
|
|
- end = self.index('insert')
|
886
|
|
-
|
887
|
|
- self.tag_add('cursorblock', start, end)
|
888
|
|
- self.mark_set('insert', self.cursor)
|
889
|
|
-
|
890
|
|
- self.after(15, self._place_cursor)
|
891
|
|
-
|
892
|
|
- def _blink_cursor(self): # theme: terminal
|
893
|
|
- '''alternate the background color of the cursorblock tagged text
|
894
|
|
- every 600 milliseconds'''
|
895
|
|
-
|
896
|
|
- if self.switch == self.fg:
|
897
|
|
- self.switch = self.bg
|
898
|
|
- else:
|
899
|
|
- self.switch = self.fg
|
900
|
|
-
|
901
|
|
- self.tag_config('cursorblock', background=self.switch)
|
902
|
1183
|
|
903
|
|
- self.after(600, self._blink_cursor)
|
904
|
1184
|
# end - output_window
|
905
|
1185
|
|
906
|
1186
|
|
|
@@ -923,6 +1203,9 @@ def main():
|
923
|
1203
|
target_env = get_env(board_name, Marlin_ver)
|
924
|
1204
|
|
925
|
1205
|
auto_build = output_window()
|
|
1206
|
+ if 0 <= target_env.find('USB1286'):
|
|
1207
|
+ copy_boards_dir() # copy custom boards over to PlatformIO if using custom board
|
|
1208
|
+ # causes 3-5 second delay in main window appearing
|
926
|
1209
|
auto_build.start_thread() # executes the "run_PIO" function
|
927
|
1210
|
|
928
|
1211
|
auto_build.root.mainloop()
|