-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathide_pyqt.py
executable file
·2685 lines (2533 loc) · 133 KB
/
ide_pyqt.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
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# NAPS: The New Age PAW-like System - Herramientas para sistemas PAW-like
#
# Entorno de desarrollo integrado (IDE), hecho con PyQt
# Copyright (C) 2010, 2018-2025 José Manuel Ferrer Ortiz
#
# *****************************************************************************
# * *
# * This program is free software; you can redistribute it and/or modify it *
# * under the terms of the GNU General Public License version 2, as *
# * published by the Free Software Foundation. *
# * *
# * 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 version 2 for more details. *
# * *
# * You should have received a copy of the GNU General Public License *
# * version 2 along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
# * *
# *****************************************************************************
from alto_nivel import *
from prn_func import _, prn
import argparse # Para procesar argumentos de línea de comandos
import functools # Para partial
import locale # Para codificar bien la salida estándar
import os # Para curdir, listdir y path
import subprocess # Para ejecutar el intérprete
import sys
import types # Para poder comprobar si algo es una función
try:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
vers_pyqt = 4
except:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
vers_pyqt = 5
dlg_abrir = None # Diálogo de abrir fichero
dlg_acerca_de = None # Diálogo "Acerca de"
dlg_banderas = None # Diálogo para consultar y modificar las banderas
dlg_contadores = None # Diálogo de contadores
dlg_fallo = None # Diálogo para mostrar fallos leves
dlg_guardar = None # Diálogo de guardar fichero
dlg_desc_locs = None # Diálogo para consultar y modificar las descripciones de localidades
dlg_desc_objs = None # Diálogo para consultar y modificar las descripciones de objetos
dlg_msg_sys = None # Diálogo para consultar y modificar los mensajes de sistema
dlg_msg_usr = None # Diálogo para consultar y modificar los mensajes de usuario
dlg_procesos = None # Diálogo para consultar y modificar las tablas de proceso
dlg_vocabulario = None # Diálogo para consultar y modificar el vocabulario
mdi_banderas = None # Subventana MDI para dlg_banderas
mdi_desc_locs = None # Subventana MDI para dlg_desc_locs
mdi_desc_objs = None # Subventana MDI para dlg_desc_objs
mdi_msg_sys = None # Subventana MDI para dlg_msg_sys
mdi_msg_usr = None # Subventana MDI para dlg_msg_usr
mdi_procesos = None # Subventana MDI para dlg_procesos
mdi_vocabulario = None # Subventana MDI para dlg_vocabulario
mdi_juego = None # Subventana MDI para la pantalla de juego del intérprete
campo_txt = None # El campo de texto del diálogo de procesos
pestanyas = None # La barra de pestañas del diálogo de procesos
mod_actual = None # Módulo de librería activo
pal_sinonimo = dict() # Sinónimos preferidos para cada par código y tipo válido
pals_no_existen = [] # Códigos de palabra que no existen encontrados en tablas de proceso
pals_salida = [] # Códigos de palabra que se usan como salida en la tabla de conexiones
color_base = QColor (10, 10, 10) # Color de fondo gris oscuro
color_pila = QColor (60, 35, 110) # Color de fondo azul brillante
color_tope_pila = QColor (35, 40, 110) # Color de fondo morado oscuro
inicio_debug = False # Para hacer la inicialización de subventanas MDI al lanzar depuración
modo_claro = False # Si se muestra la interfaz en modo claro u oscuro
pila_procs = [] # Pila con estado de los procesos en ejecución
proc_interprete = None # Proceso del intérprete
puntos_ruptura = {} # Puntos de ruptura donde pausar la ejecución, indexados por proceso
tam_fuente = 12 # El tamaño de fuente para el diálogo de procesos
# Identificadores (para hacer el código más legible) predefinidos
IDS_LOCS = {
'GAC': {
0: 'NOTCREATED',
'NOTCREATED': 0,
32767: 'CARRIED',
'CARRIED': 32767},
None: {
0: 'INITIAL',
'INITIAL': 0,
252: 'NOTCREATED',
'NOTCREATED': 252,
'_': 252,
253: 'WORN',
'WORN': 253,
254: 'CARRIED',
'CARRIED': 254,
255: 'HERE',
'HERE': 255}
}
# Conversión de teclas para manejo del intérprete
conversion_teclas = {Qt.Key_Escape: 27, Qt.Key_Down: 80, Qt.Key_End: 79, Qt.Key_Home: 71, Qt.Key_Left: 75, Qt.Key_Right: 77, Qt.Key_Up: 72}
# Estilos CSS para el diálogo de banderas
estilo_banderas = ''
estilo_cambiada = 'color: #0f0'
estilo_fila_par = ''
estilo_fila_impar = ''
# Funciones de exportación e importación, con sus módulos, extensiones y descripciones
info_exportar = []
info_importar = []
# Funciones de nueva base de datos, con sus módulos
info_nueva = []
# Pares nombre y tipos posibles que deben tener los módulos de librería
nombres_necesarios = (('acciones', dict),
('cadena_es_mayor', types.FunctionType),
('condiciones', dict),
('conexiones', (dict, list)),
('desc_locs', (dict, list)),
('desc_objs', (dict, list)),
('func_nueva', str),
('funcs_exportar', tuple),
('funcs_importar', tuple),
('escribe_secs_ctrl', types.FunctionType),
('INDIRECCION', bool),
('lee_secs_ctrl', types.FunctionType),
('locs_iniciales', (dict, list)),
('LONGITUD_PAL', int),
('msgs_sys', (dict, list)),
('msgs_usr', list),
('NOMB_COMO_VERB', list),
('NOMBRE_SISTEMA', str),
('nombres_objs', (dict, list)),
('NUM_ATRIBUTOS', list),
('NUM_BANDERAS', list),
('PREP_COMO_VERB', int),
('tablas_proceso', (dict, list)),
('TIPOS_PAL', tuple),
('vocabulario', list))
argparse._ = _ # Para la localización de textos
class BarraIzquierda (QWidget):
"""Barra vertical del margen izquierdo del campo de texto, para puntos de ruptura"""
anchoBarra = 20 # Ancho en píxeles de esta barra
diametro = 16 # Diámetro del círculo indicador de punto de ruptura
margenHor = 2 # Margen horizontal para el indicador de punto de ruptura
def __init__ (self, parent):
QWidget.__init__ (self, parent)
def enterEvent (self, evento):
selector.statusBar().showMessage (_('Adds or removes breakpoints'))
def leaveEvent (self, evento):
selector.statusBar().clearMessage()
def mousePressEvent (self, evento):
"""Añade o elimina punto de ruptura al pulsar el ratón en la barra"""
linea = campo_txt.cursorForPosition (evento.pos()).block()
numEntrada, posicion = campo_txt._daNumEntradaYLinea (linea)
if posicion == 1: # Es la línea en blanco tras la cabecera de entrada
return
numProceso = pestanyas.currentIndex()
proceso = mod_actual.tablas_proceso[numProceso] # El proceso de la posición pulsada
if not proceso[0]: # El proceso no tiene entradas
return
entrada = proceso[1][numEntrada] # La entrada de la posición pulsada
numCondacto = max (-1, posicion - 2)
if numCondacto >= len (entrada): # Es alguna de las líneas en blanco al final de la entrada
return
puntoRuptura = (numEntrada, numCondacto, posicion)
if numProceso not in puntos_ruptura:
puntos_ruptura[numProceso] = []
if puntoRuptura in puntos_ruptura[numProceso]:
puntos_ruptura[numProceso].remove (puntoRuptura)
else:
puntos_ruptura[numProceso].append (puntoRuptura)
puntos_ruptura[numProceso].sort() # Se necesita ordenado
enviaPuntoRuptura (numProceso, numEntrada, numCondacto)
self.update()
def paintEvent (self, evento):
painter = QPainter (self)
painter.fillRect (evento.rect(), Qt.lightGray if modo_claro else Qt.darkGray)
numProceso = pestanyas.currentIndex()
if numProceso not in puntos_ruptura or not puntos_ruptura[numProceso]:
return
linea = campo_txt.cursorForPosition (QPoint (0, 0)).block()
numEntrada, posicion = campo_txt._daNumEntradaYLinea (linea)
# Saltamos puntos de ruptura anteriores a esta posición (están ordenados)
for p in range (len (puntos_ruptura[numProceso])):
if puntos_ruptura[numProceso][p][0] > numEntrada or (puntos_ruptura[numProceso][p][0] == numEntrada and puntos_ruptura[numProceso][p][2] >= posicion):
break
else: # No hay puntos de ruptura en este proceso desde la primera línea visible en adelante
return
# Detectamos si al inicio del campo de texto hay una línea incompleta o margen antes de la primera línea
alturaLinea = tam_fuente # Sirve como cota inferior
for i in range (1, alturaLinea * 2, 2):
sigLinea = campo_txt.cursorForPosition (QPoint (0, i)).block()
sigPosicion = campo_txt._daNumEntradaYLinea (sigLinea)[1]
if sigPosicion != posicion: # Ya es la siguiente línea
break
else: # Margen superior del campo de texto mayor de lo esperado
# Puede ocurrir mientras se redibuja el campo de texto, y cuando la línea ocupa varias
i = alturaLinea + 1
# Dibujamos donde corresponda los marcadores de punto de ruptura visibles
margenSup = i - alturaLinea - 1 # Espacio que precede a la primera línea, por margen o por estar incompleta
lineasVisibles = ((self.size().height() - abs (margenSup)) // alturaLinea) + (1 if margenSup < 0 else 0) + 1
painter.setBrush (Qt.red)
l = 0 # Índice de línea actual
coordY = margenSup # Coordenada y que se comprobará para saber qué línea es
posAntes = -2 # Posición encontrada anteriormente, para distinguir cuándo cambia la línea
while p < len (puntos_ruptura[numProceso]) and l < lineasVisibles:
linea = campo_txt.cursorForPosition (QPoint (0, max (0, coordY))).block()
numEntrada, posicion = campo_txt._daNumEntradaYLinea (linea)
if posicion == posAntes: # Todavía es la misma línea
coordY += alturaLinea
continue
posAntes = posicion
if puntos_ruptura[numProceso][p][0] == numEntrada and puntos_ruptura[numProceso][p][2] == posicion:
# Buscamos exactamente el primer píxel donde empieza
y = coordY
for y in range (coordY - 1, max (0, coordY - 2 * alturaLinea), -1):
linea = campo_txt.cursorForPosition (QPoint (0, y)).block()
if campo_txt._daNumEntradaYLinea (linea)[1] != posicion:
break
# Dibujamos el marcador de punto de ruptura
painter.drawEllipse (self.margenHor, y + (alturaLinea - self.diametro) // 2, self.diametro, self.diametro)
p += 1
l += 1
def wheelEvent (self, evento):
"""Pasa el evento al campo de texto, para hacer scroll y cambiar el zoom como si fuera el mismo widget"""
campo_txt.wheelEvent (evento)
class CampoTexto (QTextEdit):
"""Campo de texto para las tablas de proceso"""
def __init__ (self, parent):
QTextEdit.__init__ (self, parent)
self.actualizandoProceso = QTimer()
self.actualizandoProceso.setSingleShot (True)
self.actualizandoProceso.timeout.connect (actualizaProceso)
self.barra = BarraIzquierda (self)
self.setViewportMargins (self.barra.anchoBarra, 0, 0, 0)
# Construimos variables necesarias para la introducción de condactos
self.condactos = {} # Información completa de los condactos indexada por nombre
self.condactosPorCod = {} # Nombre de condactos indexado por código
self.listaAcciones = set() # Lista de nombres de acciones
self.listaCondiciones = set() # Lista de nombres de condiciones
for codigo, condacto in mod_actual.acciones.items():
if mod_actual.NOMBRE_SISTEMA == 'QUILL':
codigo += 100
self.listaAcciones.add (condacto[0])
self.condactos[condacto[0]] = condacto + (codigo,)
self.condactosPorCod[codigo] = condacto[0]
for codigo, condacto in mod_actual.condiciones.items():
self.listaCondiciones.add (condacto[0])
self.condactos[condacto[0]] = condacto + (codigo,)
self.condactosPorCod[codigo] = condacto[0]
def _daColsValidas (self, textoLinea):
"""Devuelve las posiciones válidas para el cursor en la línea de tabla de proceso con texto dado"""
colsValidas = []
espacioAntes = False
for c in range (len (textoLinea)):
if textoLinea[c] == ' ':
espacioAntes = True
continue
if espacioAntes:
if textoLinea[c] == '"':
colsValidas.append (c + 1)
break # Dejamos de buscar nada más tras encontrar comillas
colsValidas.append (c)
espacioAntes = False
colsValidas.append (len (textoLinea))
return colsValidas
def _daColValida (self, numColumna, textoLinea, colsValidas = None):
"""Devuelve la posición válida más cercana para el cursor en la columna dada de la línea de tabla de proceso con texto dado"""
if colsValidas == None:
colsValidas = self._daColsValidas (textoLinea)
if numColumna in colsValidas:
return numColumna # Era válida
# Buscamos la columna válida más cercana
for c in range (len (colsValidas)):
if colsValidas[c] < numColumna:
continue
if c: # No es la primera
# Nos quedamos con la más cercana de entre la de la posición actual y la anterior
if abs (numColumna - colsValidas[c]) > abs (numColumna - colsValidas[c - 1]):
return colsValidas[c - 1]
return colsValidas[c]
return colsValidas[-1] # La última es la más cercana
def _daNumEntradaYLinea (self, bloqueLinea):
"""Devuelve el número de entrada de proceso y el de la línea en la entrada del bloque QTextBlock dado"""
# Buscamos la línea de cabecera de la entrada, para obtener de allí el número de entrada del proceso
cabecera = bloqueLinea
posicion = 0 # El número de línea en la entrada donde está el cursor
if cabecera.userState() == -1:
while cabecera.previous().isValid():
cabecera = cabecera.previous()
posicion += 1
if cabecera.userState() > -1:
break
numEntrada = cabecera.userState()
return numEntrada, posicion
def centraLineaCursor (self):
"""Centra verticalmente la línea del cursor actual"""
lineasVisibles = self.size().height() / float (self.cursorRect().height())
cursor = self.textCursor()
posicion = cursor.position()
self.moveCursor (QTextCursor.End) # Vamos al final, para que al ir a la línea que toca, esa quede arriba
cursor.movePosition (QTextCursor.Up, n = int (lineasVisibles // 2) - 1)
self.setTextCursor (cursor)
cursor.setPosition (posicion)
cursor.movePosition (QTextCursor.Right, n = 2)
self.setTextCursor (cursor)
def contextMenuEvent (self, evento):
linea = self.cursorForPosition (evento.pos()).block()
numEntrada = self._daNumEntradaYLinea (linea)[0]
# prn ('Nº línea:', linea.blockNumber())
# prn ('Nº entrada guardado en la línea:', linea.userState())
# prn ('Nº entrada guardado en cabecera:', cabecera.userState())
# prn ('Texto de la línea:', linea.text())
contextual = self.createStandardContextMenu()
# Deshabilitamos al menos de momento las acciones de cortar, pegar y eliminar
for accion in contextual.actions():
if 'Ctrl+V' in accion.text() or 'Ctrl+X' in accion.text() or 'Delete' in accion.text():
accion.setEnabled (False)
menuEliminar = QMenu (_('Delete'), contextual)
accionAntes = QAction (_('Add entry &before'), contextual)
accionDespues = QAction (_('Add entry &after'), contextual)
accionElimEnt = QAction (_('This entry'), selector) # Necesario poner como padre selector...
accionElimTodo = QAction (_('All entries'), selector) # ... para que funcionen los status tips
if numEntrada == -1 or self.textCursor().hasSelection():
menuEliminar.setEnabled (False)
if self.textCursor().hasSelection():
accionAntes.setEnabled (False)
accionDespues.setEnabled (False)
accionElimTodo.setStatusTip (_('Deletes all entries in the process'))
accionElimEnt.triggered.connect (lambda: quitaEntradaProceso (numEntrada))
accionElimTodo.triggered.connect (quitaEntradasProceso)
accionAntes.triggered.connect (lambda: nuevaEntradaProceso (numEntrada))
accionDespues.triggered.connect (lambda: nuevaEntradaProceso (numEntrada + 1))
menuEliminar.addAction (accionElimEnt)
menuEliminar.addAction (accionElimTodo)
contextual.addSeparator()
contextual.addAction (accionAntes)
contextual.addAction (accionDespues)
contextual.addMenu (menuEliminar)
contextual.addAction (_('&Go to entry'), irAEntradaProceso, 'Ctrl+G')
contextual.exec_ (evento.globalPos())
def irAEntrada (self, numEntrada):
"""Mueve el cursor a la entrada dada del proceso actual, y devuelve el bloque de su línea de cabecera"""
cursor = self.textCursor()
cursor.movePosition (QTextCursor.Start)
linea = cursor.block()
if not linea.text() or linea.userState() != 0:
return # Algo inesperado: la primera línea del proceso no es la primera cabecera
entradaActual = 0
while entradaActual < numEntrada and linea.next().isValid():
linea = linea.next()
if linea.userState() > -1:
entradaActual = linea.userState()
cursor.setPosition (linea.position())
self.setTextCursor (cursor)
return linea
def irAEntradaYCondacto (self, numEntrada, numCondacto):
"""Mueve el cursor a la entrada y condacto en ésta dados del proceso actual"""
linea = self.irAEntrada (numEntrada)
if numCondacto > -1: # No se estaba comprobando encaje con la cabecera de la entrada
condactoActual = -2
while condactoActual < numCondacto and linea.next().isValid():
linea = linea.next()
condactoActual += 1
if linea.userState() > -1:
break # No debería ocurrir: hemos encontrado la cabecera de la siguiente entrada
cursor = self.textCursor()
cursor.setPosition (linea.position())
self.setTextCursor (cursor)
def keyPressEvent (self, evento):
global tam_fuente
if evento.key() in (Qt.Key_Down, Qt.Key_End, Qt.Key_Home, Qt.Key_Left, Qt.Key_Right, Qt.Key_Up):
cursor = self.textCursor()
if evento.key() in (Qt.Key_Down, Qt.Key_Up):
if evento.key() == Qt.Key_Up:
cursor.movePosition (QTextCursor.StartOfBlock) # Hace que funcione en líneas con ajuste de línea
cursor.movePosition (QTextCursor.Up)
else:
cursor.movePosition (QTextCursor.EndOfBlock) # Hace que funcione en líneas con ajuste de línea
cursor.movePosition (QTextCursor.Down)
cursor.movePosition (QTextCursor.StartOfBlock)
if cursor.block().text():
cursor.movePosition (QTextCursor.WordRight)
elif evento.key() == Qt.Key_Home:
cursor.movePosition (QTextCursor.StartOfBlock)
if cursor.block().text():
cursor.movePosition (QTextCursor.WordRight)
elif evento.key() == Qt.Key_End:
cursor.movePosition (QTextCursor.EndOfBlock)
else: # Flecha izquierda o derecha
colsValidas = self._daColsValidas (cursor.block().text())
columna = self._daColValida (cursor.positionInBlock(), cursor.block().text(), colsValidas)
posColumna = colsValidas.index (columna)
if evento.key() == Qt.Key_Left:
colNueva = colsValidas[max (0, posColumna - 1)]
else:
colNueva = colsValidas[min (posColumna + 1, len (colsValidas) - 1)]
cursor.setPosition (cursor.position() + (colNueva - columna))
self.setTextCursor (cursor)
elif evento.key() == Qt.Key_Backspace:
cursor = self.textCursor()
columna = cursor.positionInBlock()
linea = cursor.block()
numEntrada, posicion = self._daNumEntradaYLinea (linea)
numProceso = pestanyas.currentIndex()
proceso = mod_actual.tablas_proceso[numProceso] # El proceso seleccionado
entrada = proceso[1][numEntrada] # La entrada seleccionada
if posicion > 1 and posicion < len (entrada) + 2 and columna == len (linea.text()): # Sólo si está al final de una línea de condacto
del entrada[posicion - 2]
cursor.movePosition (QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
cursor.movePosition (QTextCursor.Left, QTextCursor.KeepAnchor)
self.setTextCursor (cursor)
self.cut()
# Actualizamos los puntos de ruptura necesarios de la entrada
if numProceso in puntos_ruptura:
# Quitamos punto de ruptura del condacto quitado si tenía
if (numEntrada, posicion - 2, posicion) in puntos_ruptura[numProceso]:
puntos_ruptura[numProceso].remove ((numEntrada, posicion - 2, posicion))
# Saltamos puntos de ruptura anteriores a la posición del condacto quitado
e = 0
while e < len (puntos_ruptura[numProceso]):
if puntos_ruptura[numProceso][e][0] > numEntrada or (puntos_ruptura[numProceso][e][0] == numEntrada and puntos_ruptura[numProceso][e][2] > posicion):
break
e += 1
# Actualizamos los puntos de ruptura de condactos en la entrada posteriores al quitado
for pe in range (e, len (puntos_ruptura[numProceso])):
if puntos_ruptura[numProceso][pe][0] > numEntrada:
break
puntos_ruptura[numProceso][pe] = (puntos_ruptura[numProceso][pe][0], puntos_ruptura[numProceso][pe][1] - 1, puntos_ruptura[numProceso][pe][2] - 1)
elif evento.key() == Qt.Key_Insert:
if self.overwriteMode():
self.setCursorWidth (1)
self.setOverwriteMode (False)
else:
self.setCursorWidth (int (tam_fuente * 0.7))
self.setOverwriteMode (True)
elif evento.modifiers() & Qt.ControlModifier: # Teclas de acción
if evento.key() in (Qt.Key_Minus, Qt.Key_Plus):
cursor = self.textCursor()
self.selectAll()
if evento.key() == Qt.Key_Minus:
tam_fuente -= 2
else:
tam_fuente += 2
if self.overwriteMode():
self.setCursorWidth (int (tam_fuente * 0.7))
fuente = self.font()
fuente.setPixelSize (tam_fuente)
self.setFont (fuente)
self.setTextCursor (cursor)
if accMostrarRec.isChecked(): # Recortar al ancho de línea disponible
self.actualizandoProceso.start (100)
elif evento.key() == Qt.Key_G:
irAEntradaProceso()
elif evento.key() not in (Qt.Key_V, Qt.Key_X): # Descartamos las combinaciones de pegar y cortar
super (CampoTexto, self).keyPressEvent (evento)
elif proc_interprete:
return # No se puede modificar nada cuando la BD está en ejecución
# Teclas que pueden causar modificación de la tabla de procesos
if str (evento.text()).isalpha(): # Letras
cursor = self.textCursor()
columna = cursor.positionInBlock()
linea = cursor.block()
colsValidas = self._daColsValidas (linea.text())
if columna not in (colsValidas[0], colsValidas[-1]):
return # Intentando escribir texto donde no es posible
if linea.text() and linea.userState() == -1: # Una línea de entrada que no es la cabecera
numEntrada, posicion = self._daNumEntradaYLinea (linea)
numProceso = pestanyas.currentIndex()
proceso = mod_actual.tablas_proceso[numProceso] # El proceso seleccionado
entrada = proceso[1][numEntrada] # La entrada seleccionada
if self.overwriteMode() and posicion > 1 and posicion < len (entrada) + 2:
dialogo = ModalEntrada (self, _('Choose a condact:'), evento.text(), self.condactosPorCod[entrada[posicion - 2][0]])
else:
dialogo = ModalEntrada (self, _('Choose a condact:'), evento.text())
dialogo.setComboBoxEditable (True)
condactosParaCombo = self.condactos.keys()
if mod_actual.NOMBRE_SISTEMA == 'QUILL' and len (entrada):
# Detectamos los tipos de condactos que pueden ir en esta posición
if posicion == 1 or (posicion == 2 and columna == colsValidas[0]): # Posición antes del inicio de la entrada
if entrada[0][0] < 100: # Si el primer condacto es una condición...
condactosParaCombo = self.listaCondiciones # ...sólo puede ir una condición en esta posición
elif (posicion > len (entrada) + 1) or ((posicion == len (entrada) + 1) and columna == colsValidas[-1]): # Posición tras el final de la entrada
if entrada[-1][0] >= 100: # Si el último condacto es una acción...
condactosParaCombo = self.listaAcciones # ...sólo puede ir una acción en esta posición
else: # Posición con algún condacto por delante y por detrás
indiceAntes = posicion - (2 + (1 if columna == colsValidas[0] else 0)) # Índice del condacto anterior a la posición
indiceDespues = (posicion - 2) + (1 if columna == colsValidas[-1] else 0) # Índice del siguiente condacto a la posición
# Si la entrada es válida, las dos condiciones siguientes son mutuamente exclusivas
if entrada[indiceAntes][0] >= 100: # Si el condacto anterior es una acción...
condactosParaCombo = self.listaAcciones # ...sólo puede ir una acción en esta posición
elif entrada[indiceDespues][0] < 100: # Si el siguiente condacto es una condición...
condactosParaCombo = self.listaCondiciones # ...sólo puede ir una condición en esta posición
for e in range (len (entrada)):
condacto, parametros = entrada[e]
dialogo.setComboBoxItems (sorted (condactosParaCombo))
if dialogo.exec_() == QDialog.Accepted:
nomCondacto = str (dialogo.textValue()).upper()
if nomCondacto not in self.condactos:
muestraFallo (_('Invalid condact'), _('Condact with name %s not available for this system or platform') % nomCondacto)
elif nomCondacto not in condactosParaCombo:
muestraFallo (_('Invalid condact'), _('%s cannot be entered on this position') % (_('A condition') if condactosParaCombo == self.listaAcciones else _('An action')))
else:
condacto = self.condactos[nomCondacto]
lineaNueva = [condacto[-1], [0] * len (condacto[1])] # Código y parámetros de la nueva línea
if posicion > 1: # Si no añade al inicio de la entrada
if self.overwriteMode():
cursor.movePosition (QTextCursor.EndOfBlock)
cursor.movePosition (QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
cursor.movePosition (QTextCursor.Left, QTextCursor.KeepAnchor)
parametros = entrada[posicion - 2][1] # Conservaremos parámetros anteriores
lineaNueva[1] = parametros[: len (condacto[1])] + ([0] * (max (0, len (condacto[1]) - len (parametros))))
elif columna < len (linea.text()) or posicion == len (entrada) + 2: # No es fin de línea o es final de entrada
cursor.movePosition (QTextCursor.Up)
cursor.movePosition (QTextCursor.EndOfBlock)
self.setTextCursor (cursor)
imprimeCondacto (*lineaNueva)
cursor = self.textCursor()
if posicion == 1: # Añadir al inicio de la entrada
entrada.insert (0, lineaNueva)
cursor.movePosition (QTextCursor.Down)
cursor.movePosition (QTextCursor.StartOfBlock)
cursor.movePosition (QTextCursor.WordRight)
elif posicion < len (entrada) + 2:
if self.overwriteMode(): # Modificar un condacto ya existente en la entrada, conservando parámetros
entrada[posicion - 2] = lineaNueva
elif columna < len (linea.text()): # No es fin de línea, añade antes
entrada.insert (posicion - 2, lineaNueva)
else: # Es fin de línea, añade después
entrada.insert (posicion - 1, lineaNueva)
else: # Añadir al final de la entrada
entrada.append (lineaNueva)
if not condacto[1]: # El nuevo condacto no tiene parámetros
cursor.movePosition (QTextCursor.Down)
cursor.movePosition (QTextCursor.EndOfLine)
if condacto[1]: # Si el nuevo condacto tiene parámetros, mueve cursor al primero
cursor.movePosition (QTextCursor.StartOfBlock)
cursor.movePosition (QTextCursor.WordRight, n = 2)
self.setTextCursor (cursor)
# Actualizamos los puntos de ruptura de condactos en la entrada posteriores al añadido
if numProceso in puntos_ruptura and not self.overwriteMode() and posicion < len (entrada) + 1:
# import pdb
# pdb.set_trace()
if columna >= len (linea.text()): # Era fin de línea, condacto añadido después
posicion += 1
# Saltamos puntos de ruptura anteriores a la posición del condacto añadido
e = 0
while e < len (puntos_ruptura[numProceso]):
if puntos_ruptura[numProceso][e][0] > numEntrada or (puntos_ruptura[numProceso][e][0] == numEntrada and puntos_ruptura[numProceso][e][2] >= posicion):
break
e += 1
# Actualizamos los puntos de ruptura de condactos en la entrada posteriores al añadido
for pe in range (e, len (puntos_ruptura[numProceso])):
if puntos_ruptura[numProceso][pe][0] > numEntrada:
break
puntos_ruptura[numProceso][pe] = (puntos_ruptura[numProceso][pe][0], puntos_ruptura[numProceso][pe][1] + 1, puntos_ruptura[numProceso][pe][2] + 1)
elif linea.userState() > -1: # Estamos en la cabecera
prn ('En cabecera')
elif evento.key() in (Qt.Key_At, Qt.Key_BracketLeft) and mod_actual.INDIRECCION:
cursor = self.textCursor()
columna = cursor.positionInBlock()
linea = cursor.block()
colsValidas = self._daColsValidas (linea.text())
if not linea.text().trimmed() or linea.userState() > -1:
return # Intentando cambiar indirección en línea sin condacto
numEntrada, posicion = self._daNumEntradaYLinea (linea)
numProceso = pestanyas.currentIndex()
proceso = mod_actual.tablas_proceso[numProceso] # El proceso seleccionado
entrada = proceso[1][numEntrada] # La entrada seleccionada
condacto = entrada[posicion - 2]
lineaNueva = (condacto[0] ^ 128, condacto[1]) # Alternamos indirección en el condacto
entrada[posicion - 2] = lineaNueva # Código y parámetros de la nueva línea
cursor.movePosition (QTextCursor.EndOfBlock)
cursor.movePosition (QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
cursor.movePosition (QTextCursor.Left, QTextCursor.KeepAnchor)
self.setTextCursor (cursor)
imprimeCondacto (*lineaNueva)
if columna in colsValidas: # Recuperamos posición anterior correcta
indice = colsValidas.index (columna)
linea = cursor.block()
colsValidas = self._daColsValidas (linea.text())
columna = cursor.positionInBlock()
colNueva = colsValidas[indice]
cursor.setPosition (cursor.position() + (colNueva - columna))
self.setTextCursor (cursor)
elif evento.key() >= Qt.Key_0 and evento.key() <= Qt.Key_9: # Números
cursor = self.textCursor()
columna = cursor.positionInBlock()
linea = cursor.block()
if str (cursor.selectedText()).isdigit():
columna -= len (cursor.selectedText()) # Inicio del parámetro
elif cursor.hasSelection():
return # Intentando escribir número donde no es posible
colsValidas = self._daColsValidas (linea.text())
columna = self._daColValida (columna, linea.text(), colsValidas)
if columna == colsValidas[0] or (columna == colsValidas[-1] and len (colsValidas) < 3) or linea.text()[columna - 1] == '"':
return # Intentando escribir número donde no es posible
if columna == colsValidas[-1]: # Está al final del último parámetro
columna = colsValidas[-2]
numParam = colsValidas.index (columna)
parametro = str (linea.text()[columna:])
if ',' in parametro: # Hay algún parámetro posterior
parametro = parametro[: parametro.find (',')]
elif ' ' in parametro: # Hay una anotación posterior (del texto asociado al parámetro)
parametro = parametro[: parametro.find (' ')]
dialogo = ModalEntrada (self, _('Parameter value:'), evento.text(), parametro)
dialogo.setInputMode (QInputDialog.IntInput)
dialogo.setIntRange (0, 255)
if dialogo.exec_() == QDialog.Accepted:
numEntrada, posicion = self._daNumEntradaYLinea (linea)
numProceso = pestanyas.currentIndex()
proceso = mod_actual.tablas_proceso[numProceso] # El proceso seleccionado
entrada = proceso[1][numEntrada] # La entrada seleccionada
valor = dialogo.intValue()
condacto = entrada[posicion - 2]
condacto[1][numParam - 1] = valor
cursor.movePosition (QTextCursor.EndOfBlock)
cursor.movePosition (QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
cursor.movePosition (QTextCursor.Left, QTextCursor.KeepAnchor)
self.setTextCursor (cursor)
imprimeCondacto (*condacto)
if numParam == 1 and len (condacto[1]) > 1: # Hemos cambiado el primer parámetro y hay segundo parámetro
cursor = self.textCursor()
cursor.movePosition (QTextCursor.StartOfBlock)
cursor.movePosition (QTextCursor.WordRight, n = 4) # Vamos al segundo parámetro
self.setTextCursor (cursor)
self.barra.update()
def mousePressEvent (self, evento):
if evento.button() & Qt.LeftButton and evento.modifiers() & Qt.ControlModifier: # Puede ser Ctrl+click en un enlace
enlace = self.anchorAt (evento.pos())
if enlace and enlace[:8] == 'proceso:':
self.quitaFormatoEnlace() # Quitamos el formato anterior
pestanyas.setCurrentIndex (int (enlace[8:]))
return
super (CampoTexto, self).mousePressEvent (evento)
def mouseReleaseEvent (self, evento):
if not evento.button() & Qt.LeftButton or self.textCursor().hasSelection():
return super (CampoTexto, self).mouseReleaseEvent (evento)
cursor = self.cursorForPosition (evento.pos())
bloque = cursor.block()
columna = cursor.positionInBlock()
textoLinea = bloque.text()
colNueva = self._daColValida (columna, textoLinea)
cursor.setPosition (cursor.position() + (colNueva - columna))
self.setTextCursor (cursor)
def quitaFormatoEnlace (self):
"""Quita el formato de enlace a la posición del cursor actual"""
cursor = self.textCursor()
formato = cursor.charFormat()
formato.setAnchor (False)
formato.setFontUnderline (False)
formato.setToolTip ('')
campo_txt.setCurrentCharFormat (formato)
def resizeEvent (self, evento):
super (CampoTexto, self).resizeEvent (evento)
self.barra.resize (self.barra.anchoBarra, evento.size().height()) # Actualizamos la altura de la barra izquierda
if accMostrarRec.isChecked(): # Recortar al ancho de línea disponible
try:
self.anchoAntes
except:
self.anchoAntes = evento.oldSize().width()
self.cuentaDifAncho = {} # Diccionario para contar cuántas veces se encuentra cada diferencia de anchura
self.difAutoAjuste = None # Valor de diferencia en los autoajustes al redibujar el campo de texto
# Ocurre un bucle de redimensiones por autoajustes al redibujar el campo de texto, en el cual la diferencia es siempre la misma, pero es dependiente del sistema (seguramente del estilo de la interfaz)
# Detectaremos cuál es esa diferencia por los autoajustes, y en cuanto la sepamos ignoraremos cambios de anchura de ese valor
diferencia = abs (evento.size().width() - self.anchoAntes)
if not diferencia or diferencia == self.difAutoAjuste: # No cambia o es el valor de la diferencia detectada
return # Evitamos bucle de redimensiones por autoajustes al redibujar el campo de texto
if diferencia <= 25:
if diferencia in self.cuentaDifAncho:
if self.cuentaDifAncho[diferencia] >= 14: # Debe ser éste el valor de diferencia de los autoajustes
self.cuentaDifAncho.clear()
self.difAutoAjuste = diferencia
return
self.cuentaDifAncho[diferencia] += 1
else:
self.cuentaDifAncho[diferencia] = 1
self.actualizandoProceso.start (100)
self.anchoAntes = evento.size().width()
def viewportEvent (self, evento):
resultado = super (CampoTexto, self).viewportEvent (evento)
if isinstance (evento, QMouseEvent) or isinstance (evento, QPaintEvent) or isinstance (evento, QResizeEvent) or isinstance (evento, QWheelEvent):
self.barra.update()
return resultado
def wheelEvent (self, evento):
global tam_fuente
if evento.modifiers() & Qt.ControlModifier:
cursor = self.textCursor()
self.selectAll()
if (evento.delta() if vers_pyqt < 5 else evento.angleDelta().y()) < 0:
tam_fuente = max (8, tam_fuente - 2)
else:
tam_fuente += 2
if self.overwriteMode():
self.setCursorWidth (int (tam_fuente * 0.7))
fuente = self.font()
fuente.setPixelSize (tam_fuente)
self.setFont (fuente)
self.setTextCursor (cursor)
if accMostrarRec.isChecked(): # Recortar al ancho de línea disponible
self.actualizandoProceso.start (100)
else:
super (CampoTexto, self).wheelEvent (evento)
class ManejoExportacion (QThread):
"""Hilo que ejecuta la exportación de base de datos sin bloquear la interfaz de usuario"""
cambia_progreso = pyqtSignal (int)
def __init__ (self, padre, fichero, indiceFiltroExportar):
QThread.__init__ (self, parent = padre)
self.fichero = fichero
self.indiceFiltro = indiceFiltroExportar
self.cambia_progreso.connect (self.cambiaProgreso)
if 'dlg_progreso' in mod_actual.__dict__:
del mod_actual.dlg_progreso[:] # Por si acaso quedaba algo ahí
self.dialogoProgreso = QProgressDialog (_('Optimizing database...\n\nPressing the "Cancel" button will export it without further optimizations'), _('&Cancel'), 0, 100, selector)
self.dialogoProgreso.setMinimumDuration (2000) # Que salga si va a durar más de 2 segundos
self.dialogoProgreso.setWindowTitle (_('Export database'))
mod_actual.cambia_progreso = self.cambia_progreso
mod_actual.dlg_progreso.append (self.dialogoProgreso)
else:
self.dialogoProgreso = None
def cambiaProgreso (self, valorProgreso):
"""Cambia el valor actual del progreso en el diálogo de progreso"""
if self.dialogoProgreso:
self.dialogoProgreso.setValue (valorProgreso)
def run (self):
mod_actual.__dict__[info_exportar[self.indiceFiltro][0]] (self.fichero)
if self.dialogoProgreso:
self.dialogoProgreso.close()
self.dialogoProgreso = None
del mod_actual.dlg_progreso[:]
class ManejoInterprete (QThread):
"""Maneja la comunicación con el intérprete ejecutando la base de datos"""
cambiaBanderas = pyqtSignal (object)
cambiaObjetos = pyqtSignal (object)
cambiaImagen = pyqtSignal()
cambiaPila = pyqtSignal()
def __init__ (self, procInterprete, padre):
QThread.__init__ (self, parent = padre)
self.procInterprete = procInterprete
def run (self):
"""Lee del proceso del intérprete, obteniendo por dónde va la ejecución"""
global proc_interprete
if sys.version_info[0] < 3:
inicioLista = '['
finLista = ']'
imagenCambiada = 'img'
banderasCambian = 'flg'
objetosCambian = 'obj'
esTeclaEntrada = 'key'
esTeclaPasos = 'stp'
else:
inicioLista = ord ('[')
finLista = ord (']')
imagenCambiada = b'img'
banderasCambian = b'flg'
objetosCambian = b'obj'
esTeclaEntrada = b'key'
esTeclaPasos = b'stp'
pilaProcs = []
while True:
linea = self.procInterprete.stdout.readline()
if not linea:
break # Ocurre cuando el proceso ha terminado
tituloJuego = _('Running %s') % mod_actual.NOMBRE_SISTEMA # Título para la ventana de juego
if linea[0] == inicioLista and finLista in linea: # Si es actualización del estado de la pila, avisamos de ella
pilaProcs = eval (linea)
pilas_pendientes.append (pilaProcs)
self.cambiaPila.emit()
elif linea[:3] == imagenCambiada:
self.cambiaImagen.emit()
elif linea[:3] == banderasCambian:
cambiosBanderas = eval (linea[4:])
self.cambiaBanderas.emit (cambiosBanderas)
elif linea[:3] == objetosCambian:
cambiosObjetos = eval (linea[4:])
self.cambiaObjetos.emit (cambiosObjetos)
elif linea[:3] == esTeclaEntrada:
acc1Paso.setEnabled (False)
acc10Pasos.setEnabled (False)
acc100Pasos.setEnabled (False)
acc1000Pasos.setEnabled (False)
accBanderas.setEnabled (False)
tituloJuego += _(' - Waiting for key')
elif linea[:3] == esTeclaPasos:
acc1Paso.setEnabled (True)
acc10Pasos.setEnabled (True)
acc100Pasos.setEnabled (True)
acc1000Pasos.setEnabled (True)
accBanderas.setEnabled (True)
tituloJuego += _(' - Stopped')
if mdi_juego: # Porque puede no estar lista todavía
mdi_juego.widget().setWindowTitle (tituloJuego)
# El proceso ha terminado
acc1Paso.setEnabled (False)
acc10Pasos.setEnabled (False)
acc100Pasos.setEnabled (False)
acc1000Pasos.setEnabled (False)
accBanderas.setEnabled (False)
accPasoAPaso.setEnabled (True)
menu_BD_nueva.setEnabled (len (info_nueva) > 0)
if dlg_desc_objs:
dlg_desc_objs.model().beginRemoveColumns (QModelIndex(), 2, 2) # Desaparecerá la columna de la localidad actual de los objetos
proc_interprete = None
if dlg_desc_objs:
dlg_desc_objs.model().endRemoveColumns()
if mdi_juego:
mdi_juego.close()
if pilaProcs:
ultimaEntrada = [pilaProcs[-1][0]]
if len (pilaProcs[-1]) > 1:
ultimaEntrada.extend ([pilaProcs[-1][1], -2])
pilas_pendientes.append ([ultimaEntrada])
self.cambiaPila.emit()
class ModalEntrada (QInputDialog):
"""Modal de entrada QInputDialog con el primer carácter ya introducido, para continuar en siguiente pulsación sin machacarlo"""
def __init__ (self, parent, etiqueta, textoInicial, textoOriginal = ''):
QInputDialog.__init__ (self, parent)
self.textoInicial = textoInicial
self.textoOriginal = textoOriginal
self.setLabelText (etiqueta)
self.setWindowTitle (_('Modal'))
# En versiones antiguas de Qt, traduciendo los siguientes botones no funcionaba la introducción del valor inicial
self.setCancelButtonText (_('&Cancel'))
self.setOkButtonText (_('&Accept'))
def _valorInicial (self):
combo = self.findChild (QComboBox) # Estará cuando se elige que sea desplegable
spinbox = self.findChild (QSpinBox) # Estará al editar valores enteros
if combo:
campo = combo.lineEdit()
elif spinbox:
campo = spinbox.findChild (QLineEdit)
else:
campo = self.findChild (QLineEdit)
if self.textoOriginal: # De esta manera, se podrá recuperar el valor original deshaciendo
campo.setText (self.textoOriginal)
campo.selectAll()
campo.insert (self.textoInicial)
else:
campo.setText (self.textoInicial)
def showEvent (self, evento):
QTimer.singleShot (0, self._valorInicial)
class ModalEntradaTexto (QDialog):
"""Modal de entrada de texto multilínea"""
def __init__ (self, parent, texto):
QDialog.__init__ (self, parent)
texto = daTextoImprimible (texto).replace ('\\n', '\n')
if mod_actual.NOMBRE_SISTEMA != 'DAAD': # En DAAD no hay tabulador, allí se usa \t para el código que pasa al juego bajo
texto = texto.replace ('\\t', '\t')
self.campo = QPlainTextEdit (texto, self)
layout = QVBoxLayout (self)
layout.addWidget (self.campo)
layoutBotones = QHBoxLayout()
botonAceptar = QPushButton (_('&Accept'), self)
botonCancelar = QPushButton (_('&Cancel'), self)
botonAceptar.clicked.connect (self.accept)
botonCancelar.clicked.connect (self.reject)
layoutBotones.addWidget (botonAceptar)
layoutBotones.addWidget (botonCancelar)
layout.addLayout (layoutBotones)
self.setWindowTitle (_('Edit the text'))
def daTexto (self):
"""Devuelve el texto editado"""
return mod_actual.escribe_secs_ctrl (str (self.campo.toPlainText()))
class ModeloTextos (QAbstractTableModel):
"""Modelo para las tablas de mensajes y de descripciones"""
def __init__ (self, parent, listaTextos):
QAbstractTableModel.__init__ (self, parent)
self.indicesTextos = sorted (listaTextos.keys()) if type (listaTextos) == dict else list (range (len (listaTextos)))
self.listaTextos = listaTextos
def columnCount (self, parent):
return 1
def data (self, index, role):
if role == Qt.DisplayRole:
return daTextoImprimible (self.listaTextos[self.indicesTextos[index.row()]])
def flags (self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled
def headerData (self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
if self.listaTextos in (mod_actual.msgs_sys, mod_actual.msgs_sys):
return _('Message text')
return _('Description text')
if orientation == Qt.Vertical and role == Qt.DisplayRole:
return self.indicesTextos[section] # section cuenta desde 0
return QAbstractTableModel.headerData (self, section, orientation, role)
def rowCount (self, parent):
return len (self.indicesTextos)
class ModeloLocalidades (ModeloTextos):
"""Modelo para la tabla de localidades"""
def __init__ (self, parent, listaTextos):
ModeloTextos.__init__ (self, parent, listaTextos)
def columnCount (self, parent):
return 1 + len (pals_salida)
def data (self, index, role):
if role == Qt.DisplayRole and index.column():
conexionesLocalidad = mod_actual.conexiones[self.indicesTextos[index.row()]]
for codigo, destino in conexionesLocalidad:
if codigo == pals_salida[index.column() - 1]:
return destino
return ''
return ModeloTextos.data (self, index, role)
def headerData (self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section:
# Si no está la palabra en el vocabulario, mostraremos el código
return pal_sinonimo[(pals_salida[section - 1], tipo_verbo)] if (pals_salida[section - 1], tipo_verbo) in pal_sinonimo else pals_salida[section - 1]
return ModeloTextos.headerData (self, section, orientation, role)
class ModeloObjetos (ModeloTextos):
"""Modelo para la tabla de objetos"""
def __init__ (self, parent, listaTextos):
ModeloTextos.__init__ (self, parent, listaTextos)
def columnCount (self, parent):
adicional = 1 if proc_interprete else 0 # Número de columnas adicionales, según si se está ejecutando la BD
# Parte común inicial: descripción, localidad inicial, y condicionalmente también localidad actual
if mod_actual.NUM_ATRIBUTOS[0] == 0: # Ningún tipo de atributos
if mod_actual.NOMBRE_SISTEMA == 'GAC':
return 3 + adicional # Añade columna peso
return 2 + adicional
if mod_actual.NUM_ATRIBUTOS[0] == 1: # Pseudoatributo prenda en Quill para QL
return 4 + adicional # Añade columnas: prenda, y nombre
if mod_actual.NUM_ATRIBUTOS[0] == 2: # Contenedor y prenda, además del peso
return 7 + adicional # Añade columnas: peso, contenedor, prenda, nombre, y adjetivo
# Contenedor, prenda, y 16 atributos extra, además del peso
return 7 + adicional # Añade columnas: peso, contenedor, prenda, (atributos extra), nombre, y adjetivo
def data (self, index, role):
if role == Qt.DisplayRole and index.column():
locsPredefinidas = IDS_LOCS[mod_actual.NOMBRE_SISTEMA] if mod_actual.NOMBRE_SISTEMA in IDS_LOCS else IDS_LOCS[None]
if index.column() == 1: # Localidad inicial
locInicial = mod_actual.locs_iniciales[self.indicesTextos[index.row()]]
return locsPredefinidas[locInicial] if locInicial in locsPredefinidas else locInicial
if proc_interprete and index.column() == 2: # Localidad actual, sólo mientras se está ejecutando la BD
locActual = locs_objs[self.indicesTextos[index.row()]]
return locsPredefinidas[locActual] if locActual in locsPredefinidas else locActual
adicional = 1 if proc_interprete else 0 # Número de columnas adicionales desde este punto
nombre = mod_actual.nombres_objs[self.indicesTextos[index.row()]][0]
if mod_actual.NUM_ATRIBUTOS[0] == 1:
if index.column() == 2 + adicional: # Pseudoatributo prenda en Quill para QL
return _('&Yes', 1) if 199 < nombre < 255 else _('&No', 1)
# Nombre
return (pal_sinonimo[(nombre, tipo_nombre)] if (nombre, tipo_nombre) in pal_sinonimo else nombre) if nombre < 255 else ''
atributos = mod_actual.atributos[self.indicesTextos[index.row()]]