Ampliando objeto C++ desde Python y mixins
PyQt4 es un "binding" de Qt4 para Python.
Una primera aproximación...
'__main__' es interesante. Nos permite probar esta pieza de forma totalmente aislada.
Esto existe en muchos otros sistemas, pero en C++ no es tan sencillo y directo (ni muy utilizado)
Heredamos de QPlainTextEdit y ampliamos utilizando la herencia. Que chulada esto de la OOP.
O no tanto.
Si ahora queremos añadir más cosas... lo podemos hacer también en MyPlainTextEdit. Y por si no queremos todas las cosas que se nos ocurra, las hacemos opcionales. Están ahí, pero... no siempre se ven.
Eso es una chapuza. Te puedes encontrar con una enorme clase que hace muchas cosas, de las que utilizas un 10% (o menos). Y lo peor, crecerá y crecerá hasta que no te atrevas a tocarla porque nunca seas capaz de econtrarte en esa enorme cantidad de símbolos y letras.
Para evitar gigantes monolíticos (gran chapuza), tenemos la opción de utilizar la herencia varias veces.
Tralarí -> CheckSpelling -> Tralará -> PythonSintax -> echo -> WordCompletion -> highlight -> QPlainTextEdit
Dependiende de la clase que quieras, podrías engancharte a resalte, eco, tralarí... tenemos piezas más pequeñas para mantener y te limitas a coger lo que necesitas.
O no.
¿No sería mejor...?
Tralará -> PythonSintax -> WordCompletion -> highlight -> CheckSpelling -> Tralarí -> echo -> QPlainTextEdit
Depende del contexto. En el momento del diseño no lo sabemos (no siempre y nunca si no es un desarrollo muy específico)
Así se trabaja en muchos sistemas OOP. :-(
Queremos evitar la herencia múltiple por sus riesgos. En ocasiones, simplemente está prohibido.
Podríamos utilizar signal/slot para hacer la ampliación con mayor desacoplamiento.
Qt no trabaja así. Por algo será.
Por un lado, el signal/slot hasta Qt4 no tiene un coste cpu considerable.
Tampoco sería un sistema limpio para ampliaciones de comportamiento.
Pero no estamos en C++, ni Java, ni C#... ¿Qué opciones tenemos en Python?
Deja el martillo y coge otra herramienta (aquí)
Python es un lenguaje dinámico. No sólo la definición de los tipos se establece en tiempo de ejecución, los tipos se pueden definir cambiar y ampliar en tiempo de ejecución (o simplemente más tarde).
Empecemos por el '__main__'.
Creamos una instancia de la sencilla y sosa clase QPlainTextEdit y luego le añadimos el comportamiento.
Podríamos no haberlo añadido, añadir otro comportamiento y añadir más cosas.
Esto tiene buena pinta.
Pero esto debería ser más sencillo.
Desgraciadamente no podemos hacerlo donde corresponde...
Observa como hemos tenido que utilizar un closure para realizar la ampliación. Esto es pensar de otra forma a C++, Java, C# y otros lenguajes que no tienen closures.
Los closures son útiles para muchas cosas. Este es sólo un caso concreto.
Pero sí lo podemos limpiar más. Dejando la ampliación en una sola línea. Mucho mejor.
Esto está muy bien, pero si se pudiera simplificar aún más...
Podemos utilizar los mixins (tampoco disponibles en Java, C# y otros). C++ tampoco los tiene aunque se podría utilizar la misma idea con metaprogramación de plantillas. No es muy utilizado por no dejar un código muy claro, elevar los tiempos de compilación y los mensajes crípticos de error (todavía no tenemos "concepts" y lo seguimos pagando)
Quedaría así...
No está nada mal.
Ahora le añadimos otro comportamiento, que haga eco de las teclas pulsadas
Ahora sí está mal. WithHighlight debería llamarse WithHighlightAndEcho
Eso no es lo que queríamos. Pero la solución es fácil con los mixins de Python
Y podríamos configurar nuestra nueva clase con todas las combinaciones que queramos...
Las siguienes son equivalentes...
Con dos opciones de ampliación, tenemos 4 posibles combinaciones. Si añadimos más comportamientos, las combinaciones se disparan y estaremos muy contentos de tener mixins.
Pero esto...
Es feo. Debería ser más sencillo. Cada uno debería limpiar su casa. Más sencillo de mantener.
Más aún me gusta la solución de Scala. Crear las clases al vuelo con los comportamientos que necesites en cada momento.
Y para dos comportamientos...
Y mucho mejor, más claro...
Sí, he invertido el orden de las clases respecto a los mixins de Python. Me gusta más así.
Y para conseguirlo sólo es necesario esto:
A lo que convendría añadirle una pequeña prueba de desarrollo
Y meterlo en un fichero Mixin
Todavía mejor si lo integramos en pruebas unitarias. Pero por el momento, no es crítico.
El código fuente final sería así.
Que no está nada mal.
Y podemos demostrar lo sencillo y elegante que queda la ampliación añadiendo otro comportamiento:
No soy experto en Python y no soy (por el momento) un fan del mismo.
Me gusta, y mucho. Tiene algunas características claras muy interesantes y algunas limitaciones aún más interesantes.
Pero no soy un fan porque me gustan mucho C++, Erlang, Elixir, Lisp, Scala, Python, Ruby, Go, Boo, Nemerle, Rust... (no necesariamente en este orden)
Voy a utilizar esto como ejemplo por razones poco interesantes.
- Utilizo algo de Qt en el trabajo y me viene mejor jugar con Qt que con wxWidgets (por ejemplo)
- Qt está hecho en C++ y pensado para C++ (con esteroides). Sirve como un contraste interesante de cómo hacer algunas cosas en C++ vs Python (u otros sistemas con mixins)
- Un interfaz visual es algo menos abstracto y aburrido que un caso abstracto de estudio
- Planeo hacer una aplicación con interfaz visual (sí PyQt4) para uso personal
Vamos a ampliar QPlainTextEdit para que resalte la línea actual y además, haga un par de cosas cuando se pulse una tecla.
Las mismas ideas se pueden utilizar para añadir resalte de código, autocompletar, y cualquier cosa que necesitemos.
Una primera aproximación...
from PyQt4.QtCore import * from PyQt4.QtGui import * class MyPlainTextEdit(QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) self.setFrameStyle(QFrame.NoFrame) self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) if(__name__ == '__main__'): app = QApplication([]) widget = MyPlainTextEdit() widget.show() app.exec_()
'__main__' es interesante. Nos permite probar esta pieza de forma totalmente aislada.
Esto existe en muchos otros sistemas, pero en C++ no es tan sencillo y directo (ni muy utilizado)
Heredamos de QPlainTextEdit y ampliamos utilizando la herencia. Que chulada esto de la OOP.
O no tanto.
Si ahora queremos añadir más cosas... lo podemos hacer también en MyPlainTextEdit. Y por si no queremos todas las cosas que se nos ocurra, las hacemos opcionales. Están ahí, pero... no siempre se ven.
Eso es una chapuza. Te puedes encontrar con una enorme clase que hace muchas cosas, de las que utilizas un 10% (o menos). Y lo peor, crecerá y crecerá hasta que no te atrevas a tocarla porque nunca seas capaz de econtrarte en esa enorme cantidad de símbolos y letras.
Para evitar gigantes monolíticos (gran chapuza), tenemos la opción de utilizar la herencia varias veces.
Tralarí -> CheckSpelling -> Tralará -> PythonSintax -> echo -> WordCompletion -> highlight -> QPlainTextEdit
Dependiende de la clase que quieras, podrías engancharte a resalte, eco, tralarí... tenemos piezas más pequeñas para mantener y te limitas a coger lo que necesitas.
O no.
¿No sería mejor...?
Tralará -> PythonSintax -> WordCompletion -> highlight -> CheckSpelling -> Tralarí -> echo -> QPlainTextEdit
Depende del contexto. En el momento del diseño no lo sabemos (no siempre y nunca si no es un desarrollo muy específico)
Así se trabaja en muchos sistemas OOP. :-(
Queremos evitar la herencia múltiple por sus riesgos. En ocasiones, simplemente está prohibido.
Podríamos utilizar signal/slot para hacer la ampliación con mayor desacoplamiento.
Qt no trabaja así. Por algo será.
Por un lado, el signal/slot hasta Qt4 no tiene un coste cpu considerable.
Tampoco sería un sistema limpio para ampliaciones de comportamiento.
Pero no estamos en C++, ni Java, ni C#... ¿Qué opciones tenemos en Python?
Deja el martillo y coge otra herramienta (aquí)
Python es un lenguaje dinámico. No sólo la definición de los tipos se establece en tiempo de ejecución, los tipos se pueden definir cambiar y ampliar en tiempo de ejecución (o simplemente más tarde).
from PyQt4.QtCore import * from PyQt4.QtGui import * def highlight(self): def highlight__internal(): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) #self.cursorPositionChanged.connect(self.highlight) #self.highlight() ## it's a pitty, self.highlight it's been created and it doesn't exist at this point return highlight__internal if(__name__ == '__main__'): app = QApplication([]) widget = QPlainTextEdit() #adding highlight feature widget.highlight = highlight(widget) widget.cursorPositionChanged.connect(widget.highlight) widget.highlight() #adding highlight feature widget.show() app.exec_()
Empecemos por el '__main__'.
Creamos una instancia de la sencilla y sosa clase QPlainTextEdit y luego le añadimos el comportamiento.
Podríamos no haberlo añadido, añadir otro comportamiento y añadir más cosas.
Esto tiene buena pinta.
#adding highlight feature widget.highlight = highlight(widget) widget.cursorPositionChanged.connect(widget.highlight) widget.highlight() #adding highlight feature
Desgraciadamente no podemos hacerlo donde corresponde...
#self.cursorPositionChanged.connect(self.highlight) #self.highlight() ## it's a pitty, self.highlight it's been created and it doesn't exist at this point
Los closures son útiles para muchas cosas. Este es sólo un caso concreto.
Pero sí lo podemos limpiar más. Dejando la ampliación en una sola línea. Mucho mejor.
from PyQt4.QtCore import * from PyQt4.QtGui import * def highlight(self): def highlight__internal(): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) self.cursorPositionChanged.connect(highlight__internal) highlight__internal() return highlight__internal if(__name__ == '__main__'): app = QApplication([]) widget = QPlainTextEdit() #adding highlight feature widget.highlight = highlight(widget) ##adding highlight feature widget.show() app.exec_()
Esto está muy bien, pero si se pudiera simplificar aún más...
Podemos utilizar los mixins (tampoco disponibles en Java, C# y otros). C++ tampoco los tiene aunque se podría utilizar la misma idea con metaprogramación de plantillas. No es muy utilizado por no dejar un código muy claro, elevar los tiempos de compilación y los mensajes crípticos de error (todavía no tenemos "concepts" y lo seguimos pagando)
Quedaría así...
from PyQt4.QtCore import * from PyQt4.QtGui import * class WithHighlight(QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) class MyEditComponent(WithHighlight, QPlainTextEdit): pass if(__name__ == '__main__'): app = QApplication([]) widget = MyEditComponent() widget.show() app.exec_()
No está nada mal.
Ahora le añadimos otro comportamiento, que haga eco de las teclas pulsadas
from PyQt4.QtCore import * from PyQt4.QtGui import * class WithHighlight(QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) def keyPressEvent(self, key): print(key.text() + "........") super(WithHighlight, self).keyPressEvent(key) class MyEditComponent(WithHighlight, QPlainTextEdit): pass if(__name__ == '__main__'): app = QApplication([]) widget = MyEditComponent() widget.show() app.exec_()
Ahora sí está mal. WithHighlight debería llamarse WithHighlightAndEcho
Eso no es lo que queríamos. Pero la solución es fácil con los mixins de Python
import sys from PyQt4.QtCore import * from PyQt4.QtGui import * class WithHighlight(QPlainTextEdit): def __init__(self, *args): self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) class WithEcho(QPlainTextEdit): def __init__(self, *args): pass def keyPressEvent(self, key): sys.stdout.write(key.text()) sys.stdout.flush() super(WithEcho, self).keyPressEvent(key) class MyEditComponent(WithHighlight, WithEcho, QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) WithHighlight.__init__(self, *args) WithEcho.__init__(self, *args) if(__name__ == '__main__'): app = QApplication([]) widget = MyEditComponent() widget.show() app.exec_()
Y podríamos configurar nuestra nueva clase con todas las combinaciones que queramos...
class MyEditComponent(WithEcho, WithHighlight, QPlainTextEdit):
pass
class MyEditComponent(WithHighlight, QPlainTextEdit):
pass
class MyEditComponent(WithEcho, QPlainTextEdit):
pass
class MyEditComponent(QPlainTextEdit):
pass
Las siguienes son equivalentes...
class MyEditComponent(WithEcho, WithHighlight, QPlainTextEdit):
pass
class MyEditComponent(WithHighlight, WithEcho, QPlainTextEdit):
pass
Con dos opciones de ampliación, tenemos 4 posibles combinaciones. Si añadimos más comportamientos, las combinaciones se disparan y estaremos muy contentos de tener mixins.
Pero esto...
class MyEditComponent(WithHighlight, WithEcho, QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) WithHighlight.__init__(self, *args) WithEcho.__init__(self, *args)
Es feo. Debería ser más sencillo. Cada uno debería limpiar su casa. Más sencillo de mantener.
Más aún me gusta la solución de Scala. Crear las clases al vuelo con los comportamientos que necesites en cada momento.
if(__name__ == '__main__'): app = QApplication([]) widget = mixin(QPlainTextEdit, WithEcho)() widget.show() app.exec_()
Y para dos comportamientos...
if(__name__ == '__main__'): app = QApplication([]) widget = mixin(mixin(QPlainTextEdit, WithHighlight), WithEcho)() widget.show() app.exec_()
Y mucho mejor, más claro...
if(__name__ == '__main__'): app = QApplication([]) widget = mixin(QPlainTextEdit, WithEcho, WithHighlight)() widget.show() app.exec_()
Sí, he invertido el orden de las clases respecto a los mixins de Python. Me gusta más así.
Y para conseguirlo sólo es necesario esto:
def mixin(base, *mixs): def mixin__internal(base, addition): class NewClass(addition, base): def __init__(self, *args): base.__init__(self, *args) addition.__init__(self, *args) return NewClass newClass = base for mix in mixs: newClass = mixin__internal(newClass, mix) return newClass
A lo que convendría añadirle una pequeña prueba de desarrollo
# TEST if(__name__ == '__main__'): class WithAdd: def __init__(self, *args): pass def add(self, value): return self.number + value class WithSubs: def __init__(self, *args): pass def subtract(self, value): return self.number - value class myClass: def __init__(self, number): self.number = number def test_2(): MixedClass_ = mixin(myClass, WithAdd) MixedClass = mixin(MixedClass_, WithSubs) myInstance = MixedClass(4) print myInstance.add(2) print myInstance.subtract(2) def test_n(): MixedClass = mixin(myClass, WithAdd, WithSubs) myInstance = MixedClass(40) print myInstance.add(2) print myInstance.subtract(2) test_2() test_n()
Y meterlo en un fichero Mixin
Todavía mejor si lo integramos en pruebas unitarias. Pero por el momento, no es crítico.
El código fuente final sería así.
import sys from PyQt4.QtCore import * from PyQt4.QtGui import * from Mixin import mixin class WithHighlight(QPlainTextEdit): def __init__(self, *args): self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) class WithEcho(QPlainTextEdit): def __init__(self, *args): pass def keyPressEvent(self, key): sys.stdout.write(key.text()) sys.stdout.flush() super(WithEcho, self).keyPressEvent(key) if(__name__ == '__main__'): app = QApplication([]) widget = mixin(QPlainTextEdit, WithEcho, WithHighlight)() widget.show() app.exec_()
Que no está nada mal.
Y podemos demostrar lo sencillo y elegante que queda la ampliación añadiendo otro comportamiento:
import sys from PyQt4.QtCore import * from PyQt4.QtGui import * from Mixin import mixin class WithHighlight(QPlainTextEdit): def __init__(self, *args): self.highlight() self.setLineWrapMode(QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def highlight(self): extraSelections = [] if (self.isReadOnly() is False): lineColor = QColor(Qt.red).lighter(185) selection = QTextEdit.ExtraSelection() selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) cursor = self.textCursor() last_selection_line = cursor.blockNumber() selection.cursor = cursor selection.cursor.movePosition(QTextCursor.EndOfBlock) extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) while(last_selection_line == selection.cursor.blockNumber()): if(selection.cursor.atStart()): break extraSelections.append(QTextEdit.ExtraSelection(selection)) selection.cursor.movePosition(QTextCursor.StartOfLine) selection.cursor.movePosition(QTextCursor.PreviousCharacter) self.setExtraSelections(extraSelections) class WithEcho(QPlainTextEdit): def __init__(self, *args): pass def keyPressEvent(self, key): sys.stdout.write(key.text()) sys.stdout.flush() super(WithEcho, self).keyPressEvent(key) class WithTralari(QPlainTextEdit): def __init__(self, *args): pass def keyPressEvent(self, key): sys.stdout.write(key.text()+"tralarI") sys.stdout.flush() super(WithTralari, self).keyPressEvent(key) if(__name__ == '__main__'): app = QApplication([]) widget = mixin(QPlainTextEdit, WithEcho, WithHighlight, WithTralari)() widget.show() app.exec_()
No soy experto en Python y no soy (por el momento) un fan del mismo.
Me gusta, y mucho. Tiene algunas características claras muy interesantes y algunas limitaciones aún más interesantes.
Pero no soy un fan porque me gustan mucho C++, Erlang, Elixir, Lisp, Scala, Python, Ruby, Go, Boo, Nemerle, Rust... (no necesariamente en este orden)
Comentarios