//------------------------------------------------------------------------------ // This file is part of the OpenStructure project <www.openstructure.org> // // Copyright (C) 2008-2010 by the OpenStructure authors // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License as published by the Free // Software Foundation; either version 3.0 of the License, or (at your option) // any later version. // This library 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 Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //------------------------------------------------------------------------------ /* Authors: Marco Biasini, Andreas Schenk */ #include <iostream> #include <QApplication> #include <QDebug> #include <QFontMetrics> #include <QClipboard> #include <QPainter> #include <QScrollBar> #include <QDirModel> #include <QStringList> #include "python_shell_widget.hh" #include "gutter.hh" #include "string_literal_positions.hh" #include "python_shell_text_document_layout.hh" #include "python_completer.hh" #include "path_completer.hh" #include "transition.hh" /* Authors: Marco Biasini, Andreas Schenk */ namespace ost { namespace gui { PythonShellWidget::PythonShellWidget(QWidget* parent): QPlainTextEdit(parent), tab_width_(2), highlighter_(document()), completer_(new PythonCompleter(viewport())), path_completer_(new PathCompleter(viewport())), block_edit_mode_(EDITMODE_SINGLELINE), gutter_(new Gutter(this)), output_visible_(true), completion_start_(0), completion_end_(0), block_edit_start_(document()->begin()), output_blocks_(), machine_(new StateMachine(this)), readonly_machine_(new StateMachine(this)), readwrite_state_(new State), multiline_active_state_(new State) { setLineWrapMode(QPlainTextEdit::WidgetWidth); document()->setDocumentLayout(new PythonShellTextDocumentLayout(document())); setViewportMargins(Gutter::GUTTER_WIDTH, 0, 0, 0); setUndoRedoEnabled(false); setFont(QFont("Courier",font().pointSize()+2)); QFontMetrics metrics(font()); setTabStopWidth(2*metrics.width(" ")); setMaximumBlockCount(1000000); textCursor().block().setUserState(BLOCKTYPE_ACTIVE); completer_->setWidget(viewport()); connect(&PythonInterpreter::Instance(), SIGNAL(Output(unsigned int, const QString &)), this,SLOT(AppendOutput(unsigned int, const QString &))); connect(&PythonInterpreter::Instance(), SIGNAL(Finished(unsigned int, bool)), this,SLOT(OutputFinished(unsigned int,bool))); connect(&PythonInterpreter::Instance(), SIGNAL(ErrorOutput(unsigned int, const QString &)), this,SLOT(AppendError(unsigned int, const QString &))); connect(this, SIGNAL(updateRequest(QRect, int)) , gutter_,SLOT(Update(QRect, int))); connect(completer_,SIGNAL(activated(const QString&)),this, SLOT(InsertCompletion(const QString&))); connect(completer_,SIGNAL(recomplete(const QString&)),this, SLOT(Recomplete(const QString&))); connect(this,SIGNAL(RequestCompletion(const QRect&,bool)),completer_, SLOT(complete(const QRect&,bool))); connect(this,SIGNAL(SetCompletionPrefix(const QString&)),completer_, SLOT(SetCompletionPrefix(const QString&))); path_completer_->setWidget(viewport()); connect(path_completer_,SIGNAL(activated(const QString&)),this, SLOT(InsertCompletion(const QString&))); connect(path_completer_,SIGNAL(recomplete(const QString&)),this, SLOT(Recomplete(const QString&))); connect(this,SIGNAL(RequestPathCompletion(const QRect&,bool)),path_completer_, SLOT(complete(const QRect&,bool))); connect(this,SIGNAL(SetPathCompletionPrefix(const QString&)),path_completer_, SLOT(setCompletionPrefix(const QString&))); set_block_edit_mode_(EDITMODE_SINGLELINE); setup_readonly_state_machine_(); setup_state_machine_(); } void PythonShellWidget::setup_readonly_state_machine_() { State* readonly=new State; readonly_machine_->addState(readonly); readonly_machine_->addState(readwrite_state_); readonly->addTransition((new SignalTransition(this, SIGNAL(cursorPositionChanged()), readwrite_state_, new EditPositionGuard(this,EditPositionGuard::EQUAL |EditPositionGuard::BIGGER)))); readonly->addTransition((new KeyEventTransition(QEvent::KeyPress, Qt::Key_Any, Qt::NoModifier, readwrite_state_, false))); readwrite_state_->addTransition((new SignalTransition(this, SIGNAL(cursorPositionChanged()), readonly, new EditPositionGuard(this,EditPositionGuard::SMALLER)))); readwrite_state_->addTransition((new KeyEventTransition(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, readwrite_state_, true, new EditPositionGuard(this,EditPositionGuard::EQUAL)))); connect(readonly,SIGNAL(entered()),this,SLOT(OnReadonlyEntered())); connect(readwrite_state_,SIGNAL(entered()),this,SLOT(OnReadwriteEntered())); readonly_machine_->setInitialState(readwrite_state_); readonly_machine_->start(); } void PythonShellWidget::setup_state_machine_() { #ifdef __APPLE__ QFlags<Qt::KeyboardModifier> DNG_ARROW_MODIFIERS = Qt::KeypadModifier; #else QFlags<Qt::KeyboardModifier> DNG_ARROW_MODIFIERS = Qt::NoModifier; #endif State* single_line=new State; State* multi_line_inactive=new State; State* completing=new State; State* executing=new State; State* history_up=new State; State* history_down=new State; machine_->addState(single_line); machine_->addState(multiline_active_state_); machine_->addState(multi_line_inactive); machine_->addState(completing); machine_->addState(executing); machine_->addState(history_up); machine_->addState(history_down); connect(single_line,SIGNAL(entered()),this,SLOT(OnSingleLineStateEntered())); connect(multiline_active_state_,SIGNAL(entered()),this,SLOT(OnMultiLineActiveStateEntered())); connect(multi_line_inactive,SIGNAL(entered()),this,SLOT(OnMultiLineInactiveStateEntered())); connect(history_up,SIGNAL(entered()),this,SLOT(OnHistoryUpStateEntered())); connect(history_down,SIGNAL(entered()),this,SLOT(OnHistoryDownStateEntered())); connect(executing,SIGNAL(entered()),this,SLOT(OnExecuteStateEntered())); KeyEventTransition* tr1=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, executing, true, new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR)); single_line->addTransition(tr1); KeyEventTransition* tr3=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, multiline_active_state_, true, new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE)); single_line->addTransition(tr3); connect(tr3,SIGNAL(triggered()),this,SLOT(OnEnterTransition())); single_line->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,DNG_ARROW_MODIFIERS,history_up)); single_line->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,DNG_ARROW_MODIFIERS,history_down)); KeyEventTransition* tr4=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, executing, true, new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR)); multi_line_inactive->addTransition(tr4); KeyEventTransition* tr6=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, multiline_active_state_, true, new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE)); multi_line_inactive->addTransition(tr6); connect(tr6,SIGNAL(triggered()),this,SLOT(OnEnterTransition())); multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Left,DNG_ARROW_MODIFIERS,multiline_active_state_)); multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Right,DNG_ARROW_MODIFIERS,multiline_active_state_)); multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Return,Qt::ControlModifier,multiline_active_state_)); multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,DNG_ARROW_MODIFIERS,history_up)); multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,DNG_ARROW_MODIFIERS,history_down)); KeyEventTransition* tr7=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, executing, true, new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR)); multiline_active_state_->addTransition(tr7); KeyEventTransition* tr8=new KeyEventTransition(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, multiline_active_state_, true, new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE)); multiline_active_state_->addTransition(tr8); connect(tr8,SIGNAL(triggered()),this,SLOT(OnEnterTransition())); multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Escape,Qt::NoModifier,multi_line_inactive)); multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,Qt::ControlModifier | DNG_ARROW_MODIFIERS,history_up)); multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,Qt::ControlModifier | DNG_ARROW_MODIFIERS,history_down)); history_up->addTransition(new AutomaticTransition(multi_line_inactive,new HistoryGuard(&history_,EDITMODE_MULTILINE_INACTIVE))); history_up->addTransition(new AutomaticTransition(single_line,new HistoryGuard(&history_,EDITMODE_SINGLELINE))); history_down->addTransition(new AutomaticTransition(multi_line_inactive,new HistoryGuard(&history_,EDITMODE_MULTILINE_INACTIVE))); history_down->addTransition(new AutomaticTransition(single_line,new HistoryGuard(&history_,EDITMODE_SINGLELINE))); executing->addTransition(new AutomaticTransition(single_line)); machine_->setInitialState(single_line); machine_->start(); } void PythonShellWidget::OnReadonlyEntered() { setReadOnly(true); setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard); } void PythonShellWidget::OnReadwriteEntered() { if(textCursor().position()< GetEditStartBlock().position()){ moveCursor(QTextCursor::End); } setReadOnly(false); } void PythonShellWidget::OnEnterTransition() { QTextCursor cursor=textCursor(); cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor); setTextCursor(cursor); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); bool new_indent=cursor.selectedText()==":"; cursor.movePosition(QTextCursor::StartOfLine); cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); insertPlainText(QString(QChar::ParagraphSeparator)); if(cursor.selectedText()[0].isSpace() && ! cursor.atBlockEnd()){ insertPlainText(QString(cursor.selectedText())); } if (new_indent){ insertPlainText(QString("\t")); } } void PythonShellWidget::OnSingleLineStateEntered() { set_block_edit_mode_(EDITMODE_SINGLELINE); } void PythonShellWidget::OnMultiLineActiveStateEntered() { set_block_edit_mode_(EDITMODE_MULTILINE_ACTIVE); } void PythonShellWidget::OnMultiLineInactiveStateEntered() { set_block_edit_mode_(EDITMODE_MULTILINE_INACTIVE); } void PythonShellWidget::OnHistoryUpStateEntered() { if(history_.AtEnd()){ history_.SetCurrentCommand(GetCommand(),get_block_edit_mode_()); } history_.MoveToPreviousMatch(); set_command_(history_.GetCommand()); set_block_edit_mode_(history_.GetCommandMode()); } void PythonShellWidget::OnHistoryDownStateEntered() { history_.MoveToNextMatch(); set_command_(history_.GetCommand()); set_block_edit_mode_(history_.GetCommandMode()); } void PythonShellWidget::OnExecuteStateEntered() { QTextCursor cursor=textCursor(); cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor); setTextCursor(cursor); set_block_type_(block_edit_start_,textCursor().block(),BLOCKTYPE_CODE); insertPlainText(QString(QChar::ParagraphSeparator)); QString command=GetCommand(); unsigned int id=PythonInterpreter::Instance().RunCommand(command); output_blocks_.insert(id,textCursor().block()); command=command.trimmed(); if (command.size()>0) { history_.AppendCommand(command,get_block_edit_mode_()); } insertPlainText(QString(QChar::ParagraphSeparator)); block_edit_start_=textCursor().block(); } void PythonShellWidget::SetTabWidth(int width) { tab_width_=width; QFontMetrics metrics(font()); setTabStopWidth(tab_width_*metrics.width(" ")); } int PythonShellWidget::GetTabWidth() const { return tab_width_; } void PythonShellWidget::wrap_into_function_(const QString& command) { QString tmp_command=command; tmp_command.replace(QString(QChar::LineSeparator), QString(QChar::LineSeparator)+QString("\t")); QString tmp="def func():"+QString(QChar::LineSeparator)+"\t"; tmp+=tmp_command; set_command_(tmp); QTextCursor tc=textCursor(); tc.setPosition(document()->lastBlock().position()+QString("def ").length()); tc.setPosition(document()->lastBlock().position()+ QString("def func").length(),QTextCursor::KeepAnchor); setTextCursor(tc); } void PythonShellWidget::Recomplete(const QString& completion) { InsertCompletion(completion); Complete(false); } void PythonShellWidget::InsertCompletion(const QString& completion) { QTextCursor tc = textCursor(); tc.setPosition(completion_start_); tc.setPosition(completion_end_,QTextCursor::KeepAnchor); tc.insertText(completion); setTextCursor(tc); } void PythonShellWidget::AppendOutput(unsigned int id,const QString& output) { OutputBlockList::iterator it=output_blocks_.find(id); if(it==output_blocks_.end()){ return; } if(! it->isValid()){ return; } QTextCursor cursor(*it); cursor.movePosition(QTextCursor::EndOfBlock); cursor.insertText(output); set_block_type_(output_blocks_[id],cursor.block(),BLOCKTYPE_OUTPUT); output_blocks_[id]=cursor.block(); verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); ensureCursorVisible(); repaint(); } void PythonShellWidget::AppendError(unsigned int id,const QString& output) { OutputBlockList::iterator it=output_blocks_.find(id); if(it==output_blocks_.end()){ return; } if(! it->isValid()){ return; } QTextCursor cursor(*it); cursor.movePosition(QTextCursor::EndOfBlock); cursor.insertText(output); set_block_type_(output_blocks_[id],cursor.block(),BLOCKTYPE_ERROR); output_blocks_[id]=cursor.block(); verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); ensureCursorVisible(); repaint(); } void PythonShellWidget::set_block_type_(const QTextBlock& start, const QTextBlock& end, BlockType type) { QTextBlock block=start; while(block!=end){ block.setUserState(type); block=block.next(); } block.setUserState(type); document()->markContentsDirty(start.position(),end.position()+end.length()-start.position()); } void PythonShellWidget::OutputFinished(unsigned int id, bool error) { OutputBlockList::iterator it=output_blocks_.find(id); if(it==output_blocks_.end()){ return; } if(it->isValid()){ QTextCursor cursor(*it); cursor.movePosition(QTextCursor::Left,QTextCursor::KeepAnchor); cursor.removeSelectedText(); } output_blocks_.erase(it); } void PythonShellWidget::set_command_(const QString& command) { QTextCursor cursor(block_edit_start_); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.removeSelectedText(); if(block_edit_start_.isValid()){ block_edit_start_=document()->lastBlock(); } cursor.insertText(command); QAbstractTextDocumentLayout* layout=document()->documentLayout(); dynamic_cast<PythonShellTextDocumentLayout*>(layout)->EmitSizeChange(); verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); ensureCursorVisible(); } void PythonShellWidget::Complete(bool inline_completion) { QTextCursor cursor=textCursor(); if(cursor.position()<document()->lastBlock().position()){ return; } StringLiteralPositions* positions=dynamic_cast<StringLiteralPositions*>(document()->lastBlock().userData()); int literal_index=-1; if(positions){ literal_index=positions->GetStringLiteralIndex(textCursor().position()); } if (literal_index>=0) { cursor.setPosition(positions->GetStart(literal_index)); cursor.setPosition(positions->GetEnd(literal_index), QTextCursor::KeepAnchor); completion_start_=positions->GetStart(literal_index); completion_end_=positions->GetEnd(literal_index); QTextCursor cp=textCursor(); QString text=cursor.selectedText(); int sep=text.lastIndexOf(QDir::separator()); cp.setPosition(positions->GetStart(literal_index)); if (sep!=-1) { cp.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, sep); } if (QDir(text).isAbsolute()) { emit SetPathCompletionPrefix(text); } else { emit SetPathCompletionPrefix(QDir::currentPath()+QDir::separator()+text); } emit RequestPathCompletion(cursorRect(cp), inline_completion); } else { cursor.clearSelection(); cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); QString command=cursor.selectedText(); QRegExp rx("[\\w,\\.]*$"); int index=rx.indexIn(command); completion_start_=cursor.position()+index; completion_end_=cursor.position()+index+rx.cap().size(); emit SetCompletionPrefix(rx.cap()); // determine position for completion popup. This is basically the position // of the first character after a dot or the beginning of the completion // prefix if there is no dot. QTextCursor cp=textCursor(); int dot=command.lastIndexOf('.'); int left_shift=rx.cap().size(); if (dot>=index) { left_shift=rx.cap().size()-(dot-index); } cp.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, left_shift); emit RequestCompletion(cursorRect(cp), inline_completion); } } bool PythonShellWidget::handle_custom_commands_(QKeyEvent* event) { if ((event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) && event->key() == Qt::Key_H) { set_output_visible_(!output_visible_); return true; } if ((event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) && event->key() == Qt::Key_W) { wrap_into_function_(textCursor().selectedText()); return true; } // custom handling of CTRL+A to select only text in edit or output section if (event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_A) { QTextCursor cursor=textCursor(); if(cursor.position()<GetEditStartBlock().position()){ // select all output cursor.setPosition(0); cursor.setPosition(GetEditStartBlock().position(),QTextCursor::KeepAnchor); setTextCursor(cursor); }else{ //select whole edit section cursor.setPosition(GetEditStartBlock().position()); cursor.movePosition(QTextCursor::End,QTextCursor::KeepAnchor); setTextCursor(cursor); } return true; } return false; } bool PythonShellWidget::handle_completion_(QKeyEvent* event) { if(event->key() == Qt::Key_Tab){ QRegExp rx("^\\s*$"); // only white spaces from beginning of line QTextCursor cursor= textCursor(); cursor.clearSelection(); cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); QString command=cursor.selectedText(); if(!command.contains(rx)){ Complete(); return true; } } return false; } QTextBlock PythonShellWidget::GetEditStartBlock() { return block_edit_start_; } void PythonShellWidget::keyPressEvent(QKeyEvent* event) { if (this->handle_custom_commands_(event)){ return; } if (this->handle_completion_(event)){ return; } QPlainTextEdit::keyPressEvent(event); } void PythonShellWidget::mouseReleaseEvent(QMouseEvent * event) { QTextCursor mouse_cursor=cursorForPosition(event->pos()); if (get_block_edit_mode_()==EDITMODE_MULTILINE_INACTIVE && event->button() == Qt::LeftButton && mouse_cursor.position()>=document()->lastBlock().position()) { // switch to active edit mode upon mouse click in edit section set_block_edit_mode_(EDITMODE_MULTILINE_ACTIVE); } if (event->button() == Qt::MidButton && mouse_cursor.position()<document()->lastBlock().position()) { // fix cursor position before inserting text setTextCursor(mouse_cursor); return; } QPlainTextEdit::mouseReleaseEvent(event); } void PythonShellWidget::set_block_edit_mode_(BlockEditMode flag) { block_edit_mode_=flag; if(flag==EDITMODE_MULTILINE_ACTIVE){ set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_BLOCKEDIT); }else if(flag==EDITMODE_MULTILINE_INACTIVE){ set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_ACTIVE); }else { set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_ACTIVE); } gutter_->update(); } BlockEditMode PythonShellWidget::get_block_edit_mode_() { return block_edit_mode_; } QString PythonShellWidget::GetCommand() { QTextCursor cursor(block_edit_start_); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); QString text= cursor.selectedText(); text.replace(QChar::ParagraphSeparator,"\n"); return text; } void PythonShellWidget::insertFromMimeData(const QMimeData * source) { if(!source->hasText()){ return; } QString text=source->text(); readonly_machine_->setActive(readwrite_state_); text=text.replace("\r\n", QString(1, QChar::LineSeparator)) .replace('\n', QChar::LineSeparator) .replace('\r', QChar::LineSeparator); // replace spaces at the beginning of lines by tabs, because spaces are evil! QStringList lines=text.split(QChar::LineSeparator); for (int i=0; i <lines.size(); ++i) { int num_spaces=0; for (int j=0; j<lines[i].size()-1; j+=2) { if (lines[i][j]==' ' && lines[i][j+1]==' ') { num_spaces+=1; } else { break; } } QString right=lines[i].right(lines[i].size()-2*num_spaces); lines[i]=QString(num_spaces, '\t')+right; } text=lines.join(QString(1, QChar::LineSeparator)); int line_sep=text.count(QChar::LineSeparator); if(line_sep>0){ machine_->setActive(multiline_active_state_); } QPlainTextEdit::insertFromMimeData(source); } GutterBlockList PythonShellWidget::GetGutterBlocks(const QRect& rect) { GutterBlockList result; for (QTextBlock i=firstVisibleBlock(); i!=document()->end(); i=i.next()) { QRectF bounds=blockBoundingGeometry(i); QRectF geom=bounds.translated(contentOffset()); if (geom.top()<=rect.bottom() && (geom.top()>=rect.top() || geom.bottom() <= rect.bottom() || (geom.top() <= rect.top() && geom.bottom() >= rect.bottom())) ) { BlockType bt=static_cast<BlockType>(i.userState()); result.push_back(GutterBlock(geom.top(),geom.bottom(),bt)); } else { break; } } return result; } void PythonShellWidget::set_output_visible_(bool flag) { output_visible_=flag; for (QTextBlock i=document()->begin(); i!=document()->end(); i=i.next()) { if(i.userState()==BLOCKTYPE_ERROR || i.userState()==BLOCKTYPE_OUTPUT){ i.setVisible(flag); i.setLineCount(flag ? qMax(1, i.layout()->lineCount()) : 0); } } dynamic_cast<PythonShellTextDocumentLayout*>(document()->documentLayout())->requestUpdate(); dynamic_cast<PythonShellTextDocumentLayout*>(document()->documentLayout())->EmitSizeChange(); ensureCursorVisible(); repaint(); gutter_->update(); viewport()->update(); this->update(); } void PythonShellWidget::resizeEvent(QResizeEvent* event) { QPlainTextEdit::resizeEvent(event); QRect cr=contentsRect(); gutter_->setGeometry(QRect(cr.left(), cr.top(),Gutter::GUTTER_WIDTH, cr.height())); } void PythonShellWidget::showEvent(QShowEvent* event) { static bool init=false; if(!init) { init=true; } } }}