PyQT4 QTextBrowser auto-scroll - python

I need a little help here.
I have this QTextBrowser where I redirect all stdout to it.
self.console_window = QtGui.QTextBrowser()
self.console_window.setReadOnly(True)
What I need now is to auto scroll to the bottom so I can see what is happening without the need of manually scroll to the bottom.
I tried this
scrollBar = self.console_window.verticalScrollBar()
scrollBar.setValue(scrollBar.maximum())
but is not working.
Any thoughts?
FIXED!!!
def handleOutput(self, text, stdout):
self.console_window.moveCursor(QtGui.QTextCursor.End)
self.console_window.ensureCursorVisible()
self.console_window.insertPlainText(text)
def Console_Window(self):
self.console_window = QtGui.QTextEdit()
self.console_window.setReadOnly(True)

just a quick update in Pyqt5.
i did it bit differently, as i've seen that a delay is needed:
self.scrollbar = self.log_output.verticalScrollBar() #the self.scrollbar is the same as your self.console_window
try:
time.sleep(0.1) #needed for the refresh
self.scrollbar.setValue(10000) #try input different high value
except:
pass #when it is not available

Related

Menubar sometimes does not become un-greyed when QFileDialog closes

OS: W10. This may be significant. If you have different results on a different platform, feedback would be helpful.
Here is an MRE. If you run it and go Ctrl+O, the menu labels become greyed. If you select a file in the QFileDialog by clicking the "Open" button or using its mnemonic (Alt+O), the open-file dialog is dismissed and the "Files" and "Help" menus become un-greyed.
However, if you go Ctrl+O again, and this time enter the name of a file in the "File name" box (QLineEdit), and then press Return, the dialog is dismissed (with a successful selection result) but the "Files" and "Help" menus remain greyed-out. It looks like this:
import sys, os
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Greying of menus MRE')
self.setGeometry(QtCore.QRect(100, 100, 400, 200))
menubar = QtWidgets.QMenuBar(self)
self.setMenuBar(menubar)
self.files_menu = QtWidgets.QMenu('&Files', self)
menubar.addMenu(self.files_menu)
self.help_menu = QtWidgets.QMenu('&Help', self)
menubar.addMenu(self.help_menu)
self.new_action = QtWidgets.QAction('&New', self)
self.files_menu.addAction(self.new_action)
self.open_action = QtWidgets.QAction('&Open', self)
self.files_menu.addAction(self.open_action)
self.open_action.setShortcut("Ctrl+O")
self.open_action.triggered.connect(self.open_file)
def focusInEvent(self, event ):
print('main_window focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('main_window focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('main_window activateWindow')
super().activateWindow()
def open_file(self):
print('open file')
main_window_self = self
# open_doc_dialog = QtWidgets.QFileDialog(self.get_main_window())
class OpenDocFileDialog(QtWidgets.QFileDialog):
def accepted(self):
print('accepted')
super().accepted()
def accept(self):
print('accept')
super().accept()
def close(self):
print('close')
super().close()
def done(self, r):
print(f'done r {r}')
# neither of these solves the problem:
# main_window_self.activateWindow()
# main_window_self.files_menu.activateWindow()
super().done(r)
def hide(self):
print(f'hide')
super().hide()
def focusInEvent(self, event ):
print('focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('activateWindow')
super().activateWindow()
open_doc_dialog = OpenDocFileDialog(self)
open_doc_dialog.setWindowTitle('Choose file')
open_doc_dialog.setDirectory(os.getcwd())
# we cannot use the native dialog, because we need control over the UI
options = open_doc_dialog.Options(open_doc_dialog.DontUseNativeDialog)
open_doc_dialog.setOptions(options)
open_doc_button = open_doc_dialog.findChild(QtWidgets.QDialogButtonBox).button(QtWidgets.QDialogButtonBox.Open)
lineEdit = open_doc_dialog.findChild(QtWidgets.QLineEdit)
# this does not solve the problem
# lineEdit.returnPressed.disconnect()
# lineEdit.returnPressed.connect(open_doc_button.click)
print(f'open_doc_button {open_doc_button}, lineEdit {lineEdit}')
# show the dialog
dialog_code = open_doc_dialog.exec()
if dialog_code != QtWidgets.QDialog.Accepted: return
sel_files = open_doc_dialog.selectedFiles()
print(f'sel_files: {sel_files}')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
This problem can be understood, if not solved, with reference to this answer.
Note that this greying-out is not disablement. As explained in the above link, this has to do with "active/inactive states" of the menus (or their labels). The menus remain enabled throughout, although in this case it's impossible to know that while the open-file dialog is showing because it is modal. Clicking on one menu after the dialog has gone, or just hovering over it, is enough to un-grey them both...
The explanation, as I understand it, is that the "File name" box QLineEdit has a signal, returnPressed, which appears to activate something subtley different to the slot which is invoked when you use the "Choose" button. You can see I have experimented with trying to re-wire that signal, to no avail.
The method done of the QFileDialog appears to be called however the dialog closes (unlike close!), so I tried "activating" the main window... and then the individual QMenus... Doesn't work.
I am not clear how to get a handle on this "active state" business or why the slot connected to returnPressed is (seemingly) unable to give the "active state" back to the menus when the other slot manages to do so.
Edit
Searching on Musicamante's "unpolishing" suggestion led me to this:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
lineEdit.returnPressed.connect(return_pressed)
... unfortunately this doesn't work.
This looks like a possible Windows-related bug, since I can't reproduce it on Linux. As a work-around, you could try forcing a repaint after the dialog closes:
# show the dialog
dialog_code = open_doc_dialog.exec()
self.menubar.repaint()
Finally got it, thanks to Musicamante's suggestion:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
main_window_self.menubar.repaint()
lineEdit.returnPressed.connect(return_pressed)
... I actually tried this several times, just to make sure it was doing what was intended. So in fact, fortunately, no single-shot timer was needed in this case.

Wizard-like user interface with buttons next and back

I'm developing an application using the Qt Designer and PyQt4, I need to make several screens where each screen I capture user-specific data, for that I need to implement a next button and a back button similar to
where the current screen closes and the following opens when the user clicks next or if he clicks back, the screen closes and opens the previous screen, I made an example with only the next buttons and back to exemplify, if I was not clear:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Frm(QWidget):
def __init__(self, parent = None):
super(Frm, self).__init__(parent)
next = QPushButton('Next >', self)
back = QPushButton('< Back', self)
hbox = QHBoxLayout()
hbox.addWidget(back)
hbox.addWidget(next)
self.setLayout(hbox)
if __name__ == '__main__':
import sys
root = QApplication(sys.argv)
app = Frm(None)
app.show()
root.exec_()
In short: How do I implement a function that calls another screen and close the current at the click of a button?
First about a misconception: you do usually not create/show one screen (window) and close another, you usually only exchange the content of a wizard-like dialog window upon actions like pressing the buttons. The window is alive the whole time until the multiple page task is finished.
So I take it your question is really about:
How to exchange a widget in a layout?
Since you may still use PyQt4 which does not yet have QLayout.replaceWidget, it's best to just use methods removeWidget and addWidget of QLayout and since addWidget adds a widget to the end of the layout items list, I prefer a dedicated layout just for the interchangeable content of your wizard (see also: How to add an Item to the specific index in the layout).
Example code using PyQt5 but easily transferrable to PyQt4. Only the next button is implemented.
from PyQt5 import QtWidgets
class MyWizard(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# vertical layout, wraps content layout and buttons layout
vertical_layout = QtWidgets.QVBoxLayout()
self.setLayout(vertical_layout)
# content widget and layout
self.content_layout = QtWidgets.QVBoxLayout() # could be almost any layout actually
self.content = QtWidgets.QLabel('Page1') # customize with your content
self.content_layout.addWidget(self.content)
vertical_layout.addLayout(self.content_layout)
# back, forward buttons wraped in horizontal layout
button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch()
back_button = QtWidgets.QPushButton('Back')
back_button.clicked.connect(self.back_button_clicked)
button_layout.addWidget(back_button)
forward_button = QtWidgets.QPushButton('Forward')
forward_button.clicked.connect(self.forward_button_clicked)
button_layout.addWidget(forward_button)
vertical_layout.addLayout(button_layout)
def back_button_clicked(self):
"""
The back button is clicked.
"""
pass
def forward_button_clicked(self):
"""
The forward button is clicked.
"""
# remove old content
self.content_layout.removeWidget(self.content)
self.content.deleteLater()
# create new content
self.content = QtWidgets.QLabel('Another Page')
# add new content
self.content_layout.addWidget(self.content)
app = QtWidgets.QApplication([])
wizard = MyWizard()
wizard.setWindowTitle('MyWizard Example')
wizard.setFixedSize(600, 400)
wizard.show()
app.exec_()
And it looks like:
However, as already written in the comment by Marius, there is quite extensive support for such dialogs in Qt using QWizard. So I strongly recommend to use that instead. The example above is only to demonstrate the ability of inserting and removing widgets in layouts.
You should definitely use QWizard for such problems!
There is a QWizard class that allows you to create wizards in Qt and PyQt. It implements all the functionality you want, and lots more. All you do is design your pages by extending QWizardPage, and add them to the wizard. This is much simpler than doing the whole thing from scratch as you propose.
Old question but missing example with existing API
You don't need to create all structure of an Wizard by yourself. Qt (and PyQt) already provides a class called QWizard.
Basic example:
wizard = QtWidgets.QWizard()
page1 = QtWidgets.QWizardPage()
page1.setTitle('Page 1 is best!')
page1.setSubTitle('1111111111')
lineEdit = QtWidgets.QLineEdit()
hLayout1 = QtWidgets.QHBoxLayout(page1)
hLayout1.addWidget(lineEdit)
wizard.addPage(page1)
Complete example and some explanation:
https://www.youtube.com/watch?v=kTJ1QULxXjg
https://impatientprogrammer.net/2018/07/06/pyside-pyqt-qwizard-in-3-minutes/

wx.EVT_MAXIMIZE prevented exiting full screen

I have a frame with one radio box to toggle full screen. The frame is to go full screen when the user clicks the Maximize button. However, if I use the maximize button, the radio box would then fail to restore the window. If I use the radio box to go full screen, it will be able to restore the window.
import wx
class FSWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.SetSize((800, 600))
self.RadioFullScreen = wx.RadioBox(self, -1, "Display", choices=["Windowed","Full Screen"])
self.RadioFullScreen.Bind(wx.EVT_RADIOBOX, self.FS)
self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
self.Sizer = None
self.Show()
def FS(self, Event):
if self.RadioFullScreen.GetSelection():
self.ShowFullScreen(True)
else:
self.ShowFullScreen(False)
def OnMaximize(self, Event):
self.ShowFullScreen(True) # <-- Add self.Restore() or self.Maximize(False) here
self.RadioFullScreen.SetSelection(1)
App = wx.App()
frame =FSWindow(None, -1, "MainWindow")
App.MainLoop()
However, if I add self.Restore() or self.Maximize(False) before the self.ShowFullScreen(True) like I commented on the source code above, the radio buttons will work. Problem is, the window will be restored first before going full screen which is ugly. Any solution for this? Also please explain why this happened, if possible.
Running Python 2.7.9, WxPython 3.0.2 on Window 7 Professional 32-bit
It seems that ShowFullScreen is not setting some flag, so things get out of sync.
If I just use Maximize/Restore things work fine for me, i.e. following changes to your code.
def FS(self, Event):
if self.RadioFullScreen.GetSelection():
self.Maximize()
#self.ShowFullScreen(True, style=wx.FULLSCREEN_ALL)
print('done fs true')
else:
#self.ShowFullScreen(False, style=wx.FULLSCREEN_ALL)
self.Restore()
print('done fs false')
def OnMaximize(self, Event):
Event.Skip()
self.RadioFullScreen.SetSelection(1)
print('done max')
If you don't want the menu bar etc when the screen is maximized then uncomment the ShowFullScreen lines.
You are handling an event "Maximize", most of the time you want default behaviour also to happen, that is why I added Event.Skip to the OnMaximize handler - in this case it doesn't make a difference as it looks like the event is only fired after maximisation is already done.

Tkinter combobox - gracefully lose focus when click out of widget

I have a ComboBox written in Python Tkinter that makes the horrible system alert sound when you click off of it without selecting something.
For instance, when you hit the dropdown and select your item, it works fine. But if you hit the drop-down and then decide to click off, it will lose focus as expected, but it makes an alert sound. Can this be disabled in some way so it can gracefully lose focus without complaining? I'm on OSX 10.9 btw
UPDATE -
Minimally working code that produces the alert.
from Tkconstants import *
import ttk
import Tkinter
class PyPrecursor():
def __init__(self,root):
self.root = root
self.TabNotebook()
def TabNotebook(self):
self.main_notebook_frame = ttk.Notebook(self.root, name='main_notebook')
self.main_notebook_frame.enable_traversal()
self.OptionsF = ttk.Frame(self.main_notebook_frame, name='options')
self.length_options_frame = ttk.LabelFrame(
self.OptionsF, labelwidget=ttk.Label(font=('Arial', 15), text="Length Options:"))
self.hcdr3_length_label = ttk.Label(self.length_options_frame, text="HCDR3 Length")
self.HCDR3_Length = Tkinter.StringVar()
self.hcdr3_length_combo = ttk.Combobox(
self.length_options_frame, values=[i for i in xrange(16, 36)],
textvariable=self.HCDR3_Length)
self.hcdr3_length_combo.current(0)
self.length_options_frame.pack(side=TOP,fill=X,pady=5)
self.hcdr3_length_label.pack(side=LEFT)
self.hcdr3_length_combo.pack(side=LEFT,anchor=W)
self.main_notebook_frame.pack(side=TOP,expand=1,fill=BOTH,padx=10,pady=10)
self.main_notebook_frame.add(
self.OptionsF, text="Input Options", underline=0, padding=2)
self.main_notebook_frame.bind("<<NotebookTabChanged>>",self.update_)
def update_(self,event):
self.root.update()
def main():
root = Tkinter.Tk()
PyPrecursor(root)
root.mainloop()
root.update_idletasks()
if __name__ == '__main__':
main()
You might want to try this:
self.hcdr3_length_combo.bell(displayof=1)
Not sure if it should be 1 or 0 though...
If it doesn't work, maybe a containing widget throws the sound. Might want to apply it to the parent widget as well. I'm not familiar with python 2.7 and it doesn't throw a sound when I use python3 with slight modifications.
Usually when you can't find an option for a specific widget, you can find something in the general widget options. Just search "tkinter widget options" and you'll get some place like:
https://effbot.org/tkinterbook/widget.htm

Tkintertable Get selected Data

I'm using tkintertable and want to check which row is selected. Is this possible?
And I also want to sort the columns and name the columns like I want...
How can I do this?
I'm a little late, but figured I would answer this in case anyone else finds this through a google search like I did :P
There are a couple ways to do select data based on mouse clicks.
First you need to bind the mouse click to a callback function, and in this callback function you need to grab the data. You might need to bind the mouse click on release so that it grabs data when you release the mouse button.
You can use the table.get_row_clicked()/table.get_col_clicked() functions along with model.getValueAt() to do get individual cells.
To get the contents of an entire row in a dictionary where column_name = key & contents of that selected rows column = value, you need to use model.getRecordAtRow(row_index).
Example:
from Tkinter import *
from ttk import *
from tkintertable.Tables import TableCanvas
from tkintertable.TableModels import TableModel
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.model = TableModel()
self.table = TableCanvas(self, model=self.model)
self.table.createTableFrame()
root.bind('<ButtonRelease-1>', self.clicked) #Bind the click release event
self.create_widgets()
def create_widgets(self):
self.table.model.load('save.table') #You don't have to load a model, but I usually
self.table.redrawTable() #Create a base model for my tables.
d = dir(self.table) #Will show you what you can do with tables. add .model
#to the end to see what you can do with the models.
for i in d:
print i
def clicked(self, event): #Click event callback function.
#Probably needs better exception handling, but w/e.
try:
rclicked = self.table.get_row_clicked(event)
cclicked = self.table.get_col_clicked(event)
clicks = (rclicked, cclicked)
print 'clicks:', clicks
except:
print 'Error'
if clicks:
#Now we try to get the value of the row+col that was clicked.
try: print 'single cell:', self.table.model.getValueAt(clicks[0], clicks[1])
except: print 'No record at:', clicks
#This is how you can get the entire contents of a row.
try: print 'entire record:', self.table.model.getRecordAtRow(clicks[0])
except: print 'No record at:', clicks
root = Tk()
root.title('Table Test')
app = Application(master=root)
print 'Starting mainloop()'
app.mainloop()
As far as the other things, you should be able to get info off the wiki:
http://code.google.com/p/tkintertable/wiki/Usage
Some things are a pain to figure out, but it's worth it IMO. I still can't figure out how to programmatically change the contents of a cell, and that's right on the wiki page, lol.

Categories