7 Module implementing the QRegularExpression wizard dialog. |
7 Module implementing the QRegularExpression wizard dialog. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 import re |
11 import re |
12 |
12 import sys |
13 from PyQt4.QtCore import QFileInfo, pyqtSlot, qVersion |
13 import json |
14 try: |
14 |
15 from PyQt4.QtCore import QRegularExpression |
15 from PyQt4.QtCore import QFileInfo, pyqtSlot, qVersion, QProcess, QByteArray |
16 AVAILABLE = True |
|
17 except ImportError: |
|
18 AVAILABLE = False |
|
19 from PyQt4.QtGui import QWidget, QDialog, QInputDialog, QApplication, QClipboard, \ |
16 from PyQt4.QtGui import QWidget, QDialog, QInputDialog, QApplication, QClipboard, \ |
20 QTextCursor, QDialogButtonBox, QVBoxLayout, QTableWidgetItem |
17 QTextCursor, QDialogButtonBox, QVBoxLayout, QTableWidgetItem |
21 |
18 |
22 from E5Gui import E5MessageBox, E5FileDialog |
19 from E5Gui import E5MessageBox, E5FileDialog |
23 from E5Gui.E5MainWindow import E5MainWindow |
20 from E5Gui.E5MainWindow import E5MainWindow |
66 self.undoButton.setIcon(UI.PixmapCache.getIcon("editUndo.png")) |
63 self.undoButton.setIcon(UI.PixmapCache.getIcon("editUndo.png")) |
67 self.redoButton.setIcon(UI.PixmapCache.getIcon("editRedo.png")) |
64 self.redoButton.setIcon(UI.PixmapCache.getIcon("editRedo.png")) |
68 |
65 |
69 self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall |
66 self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall |
70 |
67 |
|
68 # start the PyQt5 server part |
|
69 self.__pyqt5Available = False |
|
70 self.__pyqt5Server = QProcess(self) |
|
71 self.__pyqt5Server.start(sys.executable, [ |
|
72 os.path.join(os.path.dirname(__file__), "QRegularExpressionWizardServer.py")]) |
|
73 if self.__pyqt5Server.waitForStarted(10000): |
|
74 self.__pyqt5Server.setReadChannel(QProcess.StandardOutput) |
|
75 if self.__sendCommand("available"): |
|
76 response = self.__receiveResponse() |
|
77 if response and response["available"]: |
|
78 self.__pyqt5Available = True |
|
79 |
71 self.saveButton = \ |
80 self.saveButton = \ |
72 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole) |
81 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole) |
73 self.saveButton.setToolTip(self.trUtf8("Save the regular expression to a file")) |
82 self.saveButton.setToolTip(self.trUtf8("Save the regular expression to a file")) |
74 self.loadButton = \ |
83 self.loadButton = \ |
75 self.buttonBox.addButton(self.trUtf8("Load"), QDialogButtonBox.ActionRole) |
84 self.buttonBox.addButton(self.trUtf8("Load"), QDialogButtonBox.ActionRole) |
76 self.loadButton.setToolTip(self.trUtf8("Load a regular expression from a file")) |
85 self.loadButton.setToolTip(self.trUtf8("Load a regular expression from a file")) |
77 if qVersion() >= "5.0.0" and AVAILABLE: |
86 if qVersion() >= "5.0.0" and self.__pyqt5Available: |
78 self.validateButton = self.buttonBox.addButton( |
87 self.validateButton = self.buttonBox.addButton( |
79 self.trUtf8("Validate"), QDialogButtonBox.ActionRole) |
88 self.trUtf8("Validate"), QDialogButtonBox.ActionRole) |
80 self.validateButton.setToolTip(self.trUtf8("Validate the regular expression")) |
89 self.validateButton.setToolTip(self.trUtf8("Validate the regular expression")) |
81 self.executeButton = self.buttonBox.addButton( |
90 self.executeButton = self.buttonBox.addButton( |
82 self.trUtf8("Execute"), QDialogButtonBox.ActionRole) |
91 self.trUtf8("Execute"), QDialogButtonBox.ActionRole) |
104 self.variableLabel.hide() |
113 self.variableLabel.hide() |
105 self.variableLineEdit.hide() |
114 self.variableLineEdit.hide() |
106 self.variableLine.hide() |
115 self.variableLine.hide() |
107 self.importCheckBox.hide() |
116 self.importCheckBox.hide() |
108 self.regexpTextEdit.setFocus() |
117 self.regexpTextEdit.setFocus() |
109 |
118 |
|
119 def __sendCommand(self, command, **kw): |
|
120 """ |
|
121 Private method to send a command to the PyQt5 server. |
|
122 |
|
123 @param commandDict dictionary with command string and related data (dict) |
|
124 @return flag indicating a successful transmission (boolean) |
|
125 """ |
|
126 result = False |
|
127 if command: |
|
128 commandDict = {"command": command} |
|
129 commandDict.update(kw) |
|
130 commandStr = json.dumps(commandDict) + "\n" |
|
131 data = QByteArray(commandStr.encode("utf-8")) |
|
132 self.__pyqt5Server.write(data) |
|
133 result = self.__pyqt5Server.waitForBytesWritten(10000) |
|
134 return result |
|
135 |
|
136 def __receiveResponse(self): |
|
137 """ |
|
138 Private method to receive a response from the PyQt5 server. |
|
139 |
|
140 @return response dictionary (dict) |
|
141 """ |
|
142 responseDict = {} |
|
143 if self.__pyqt5Server.waitForReadyRead(10000): |
|
144 data = bytes(self.__pyqt5Server.readAllStandardOutput()) |
|
145 responseStr = data.decode("utf-8") |
|
146 responseDict = json.loads(responseStr) |
|
147 if responseDict["error"]: |
|
148 E5MessageBox.critical(self, |
|
149 self.trUtf8("Communication Error"), |
|
150 self.trUtf8("""<p>The PyQt5 backend reported an error.</p>""" |
|
151 """<p>{0}</p>""").format(responseDict["error"])) |
|
152 responseDict = {} |
|
153 |
|
154 return responseDict |
|
155 |
|
156 def shutdown(self): |
|
157 """ |
|
158 Public method to shut down the PyQt5 server part. |
|
159 """ |
|
160 self.__sendCommand("exit") |
|
161 self.__pyqt5Server.waitForFinished(5000) |
|
162 |
110 def __insertString(self, s, steps=0): |
163 def __insertString(self, s, steps=0): |
111 """ |
164 """ |
112 Private method to insert a string into line edit and move cursor. |
165 Private method to insert a string into line edit and move cursor. |
113 |
166 |
114 @param s string to be inserted into the regexp line edit |
167 @param s string to be inserted into the regexp line edit |
391 @pyqtSlot() |
444 @pyqtSlot() |
392 def on_validateButton_clicked(self): |
445 def on_validateButton_clicked(self): |
393 """ |
446 """ |
394 Private slot to validate the entered QRegularExpression. |
447 Private slot to validate the entered QRegularExpression. |
395 """ |
448 """ |
396 if qVersion() < "5.0.0" or not AVAILABLE: |
449 if qVersion() < "5.0.0" or not self.__pyqt5Available: |
397 # only available for Qt5 |
450 # only available for Qt5 |
398 return |
451 return |
399 |
452 |
400 regex = self.regexpTextEdit.toPlainText() |
453 regexp = self.regexpTextEdit.toPlainText() |
401 if regex: |
454 if regexp: |
402 options = QRegularExpression.NoPatternOption |
455 options = [] |
403 if self.caseInsensitiveCheckBox.isChecked(): |
456 if self.caseInsensitiveCheckBox.isChecked(): |
404 options |= QRegularExpression.CaseInsensitiveOption |
457 options.append("CaseInsensitiveOption") |
405 if self.multilineCheckBox.isChecked(): |
458 if self.multilineCheckBox.isChecked(): |
406 options |= QRegularExpression.MultilineOption |
459 options.append("MultilineOption") |
407 if self.dotallCheckBox.isChecked(): |
460 if self.dotallCheckBox.isChecked(): |
408 options |= QRegularExpression.DotMatchesEverythingOption |
461 options.append("DotMatchesEverythingOption") |
409 if self.extendedCheckBox.isChecked(): |
462 if self.extendedCheckBox.isChecked(): |
410 options |= QRegularExpression.ExtendedPatternSyntaxOption |
463 options.append("ExtendedPatternSyntaxOption") |
411 if self.greedinessCheckBox.isChecked(): |
464 if self.greedinessCheckBox.isChecked(): |
412 options |= QRegularExpression.InvertedGreedinessOption |
465 options.append("InvertedGreedinessOption") |
413 if self.unicodeCheckBox.isChecked(): |
466 if self.unicodeCheckBox.isChecked(): |
414 options |= QRegularExpression.UseUnicodePropertiesOption |
467 options.append("UseUnicodePropertiesOption") |
415 if self.captureCheckBox.isChecked(): |
468 if self.captureCheckBox.isChecked(): |
416 options |= QRegularExpression.DontCaptureOption |
469 options.append("DontCaptureOption") |
417 |
470 |
418 re = QRegularExpression(regex, options) |
471 if self.__sendCommand("validate", options=options, regexp=regexp): |
419 if re.isValid(): |
472 response = self.__receiveResponse() |
420 E5MessageBox.information(self, |
473 if response and "valid" in response: |
421 self.trUtf8("Validation"), |
474 if response["valid"]: |
422 self.trUtf8("""The regular expression is valid.""")) |
475 E5MessageBox.information(self, |
|
476 self.trUtf8("Validation"), |
|
477 self.trUtf8("""The regular expression is valid.""")) |
|
478 else: |
|
479 E5MessageBox.critical(self, |
|
480 self.trUtf8("Error"), |
|
481 self.trUtf8("""Invalid regular expression: {0}""") |
|
482 .format(response["errorMessage"])) |
|
483 # move cursor to error offset |
|
484 offset = response["errorOffset"] |
|
485 tc = self.regexpTextEdit.textCursor() |
|
486 tc.setPosition(offset) |
|
487 self.regexpTextEdit.setTextCursor(tc) |
|
488 self.regexpTextEdit.setFocus() |
|
489 return |
|
490 else: |
|
491 E5MessageBox.critical(self, |
|
492 self.trUtf8("Communication Error"), |
|
493 self.trUtf8("""Invalid response received from PyQt5 backend.""")) |
423 else: |
494 else: |
424 E5MessageBox.critical(self, |
495 E5MessageBox.critical(self, |
425 self.trUtf8("Error"), |
496 self.trUtf8("Communication Error"), |
426 self.trUtf8("""Invalid regular expression: {0}""") |
497 self.trUtf8("""Communication with PyQt5 backend failed.""")) |
427 .format(re.errorString())) |
|
428 # move cursor to error offset |
|
429 offset = re.errorPatternOffset() |
|
430 tc = self.regexpTextEdit.textCursor() |
|
431 tc.setPosition(offset) |
|
432 self.regexpTextEdit.setTextCursor(tc) |
|
433 return |
|
434 else: |
498 else: |
435 E5MessageBox.critical(self, |
499 E5MessageBox.critical(self, |
436 self.trUtf8("Error"), |
500 self.trUtf8("Error"), |
437 self.trUtf8("""A regular expression must be given.""")) |
501 self.trUtf8("""A regular expression must be given.""")) |
438 |
502 |