I'm trying to handle the createWindow method in PyQt webkit. When you do this, PyQt gives the method a QWebPage.WebWindowType (or QWebPage::WebWindowType in c++). All I need is a URL to create a new window, so I'm not sure what to do with this class. The docs don't explain it very well either: http://doc.qt.digia.com/qt/qwebpage.html#WebWindowType-enum
I'm trying to intergrate this with my "Tab" class:
class Tab(QtGui.QWidget):
"""
Contains the variables and methods needed to open the Qt
based user interface and the event bus to allow interaction.
"""
def __init__(self, url, window):
"""
Set the up the following:
1) Initialise the Qt user interface.
2) Set margins.
3) Initialise the cookie jar.
4) Set the default URL.
5) Set the history buttons to disabled (there is not history yet).
5) Hide the tools menu.
6) Create the GUI event bus.
7) Set keyboard bindings.
"""
self.window = window
QtGui.QWidget.__init__(self)
self.ui = Ui_Browser()
self.ui.setupUi(self)
l = self.layout()
l.setMargin(0)
self.ui.horizontalLayout.setMargin(0)
try:
self.ui.webView.page().networkAccessManager().setCookieJar(self.window.cookies)
except:
pass
self.ui.webView.setUrl(url)
self.ui.url.setText(url.toString())
self.ui.back.setEnabled(False)
self.ui.forward.setEnabled(False)
self.ui.toolsBox.hide()
self.ui.tools.setChecked(False)
QtCore.QObject.connect(self.ui.back, QtCore.SIGNAL("clicked()"), self.back)
QtCore.QObject.connect(self.ui.forward, QtCore.SIGNAL("clicked()"), self.forward)
QtCore.QObject.connect(self.ui.refresh, QtCore.SIGNAL("clicked()"), self.refresh)
QtCore.QObject.connect(self.ui.url, QtCore.SIGNAL("returnPressed()"), self.url)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("linkClicked (const QUrl&)"), self.navigate)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("urlChanged (const QUrl&)"), self.navigate)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("titleChanged(const QString&)"), self.title)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("loadStarted()"), self.showProgressBar)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("loadProgress(int)"), self.setProgressBar)
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("loadFinished(bool)"), self.hideProgressBar)
QtCore.QObject.connect(self.ui.toolPrint, QtCore.SIGNAL("clicked()"), self.printPage)
self.ui.webView.createWindow = self.createWindow
QtGui.QShortcut(QtGui.QKeySequence("Backspace"), self, self.back)
QtGui.QShortcut(QtGui.QKeySequence("F5"), self, self.refresh)
QtGui.QShortcut(QtGui.QKeySequence("Ctrl+p"), self, self.printPage)
def url(self):
"""
Go to URL entered.
"""
self.setHistory()
url = self.ui.url.text()
if not str(url).startswith("http://"):
url = "http://" + url
self.ui.webView.setUrl(QtCore.QUrl(url))
def navigate(self, url):
"""
Navigate to page requested.
"""
self.setHistory()
self.ui.url.setText(url.toString())
def refresh(self):
"""
Refresh the page.
"""
self.ui.webView.setUrl(QtCore.QUrl(self.ui.webView.url()))
def back(self):
"""
Enable the back button.
"""
page = self.ui.webView.page()
history = page.history()
history.back()
if history.canGoBack():
self.ui.back.setEnabled(True)
else:
self.ui.back.setEnabled(False)
def forward(self):
"""
Enable the forward button.
"""
page = self.ui.webView.page()
history = page.history()
history.forward()
if history.canGoForward():
self.ui.forward.setEnabled(True)
else:
self.ui.forward.setEnabled(False)
def title(self, title):
"""
Change the title of the window.
"""
self.window.tabs.setTabText(self.window.tabs.indexOf(self), title) or (self.window.setWindowTitle(title) if self.isThisFocused() else "New Tab")
def createWindow(self, WebWindowType):
"""
Overide the default behaviour.
"""
tab = Tab(QtCore.QUrl("about:blank"), self.window)
print(mode)
if mode == QtWebKit.QWebPage.WebModalDialog:
tab.ui.webView.setWindowModality(QtCore.Qt.ApplicationModal)
self.window.tabs.setCurrentIndex(self.window.tabs.addTab(tab, ""))
def isThisFocused(self):
"""
Return whether this tab is focused or not.
"""
if self.window.tabs.currentWidget() == self:
return True
return False
def setHistory(self):
"""
Check history and update buttons.
"""
page = self.ui.webView.page()
history = page.history()
if history.canGoBack():
self.ui.back.setEnabled(True)
else:
self.ui.back.setEnabled(False)
if history.canGoForward():
self.ui.forward.setEnabled(True)
else:
self.ui.forward.setEnabled(False)
def showProgressBar(self):
"""
We're loading a page, show the load progress bar.
"""
self.ui.progress.show()
def setProgressBar(self, percent):
"""
Set the percentage of the progress bar.
"""
self.ui.progress.setValue(percent)
def hideProgressBar(self, bool):
"""
We've finished loading the page, there is no need for the progress bar.
"""
self.ui.progress.hide()
def printPage(self):
"""
Use Qt's goodness to print the page.
"""
previewer = QtGui.QPrintPreviewDialog(paintRequested=self.ui.webView.print_)
previewer.exec_()
The createWindow function is used for handling requests to open a new window (e.g. a javascript window.open() call).
By default, createWindow does nothing, so it must be reimplemented it in a subclass.
Also, to work with javascript, it is necessary that the web-settings specify that javascript programs are allowed to open new windows.
Here's a demo script that illustrates how to use createWindow:
from PyQt4 import QtGui, QtCore, QtWebKit
class Browser(QtWebKit.QWebView):
_windows = set()
#classmethod
def _removeWindow(cls, window):
if window in cls._windows:
cls._windows.remove(window)
#classmethod
def newWindow(cls):
window = cls()
cls._windows.add(window)
return window
def __init__(self, parent=None):
QtWebKit.QWebView.__init__(self, parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
self.settings().setAttribute(
QtWebKit.QWebSettings.JavascriptEnabled, True)
self.settings().setAttribute(
QtWebKit.QWebSettings.JavascriptCanOpenWindows, True)
def closeEvent(self, event):
self._removeWindow(self)
event.accept()
def createWindow(self, mode):
window = self.newWindow()
if mode == QtWebKit.QWebPage.WebModalDialog:
window.setWindowModality(QtCore.Qt.ApplicationModal)
window.show()
return window
html = """
<html>
<head>
<title>Test Page</title>
<script type="text/javascript"><!--
var url = 'https://www.google.com'
var count = 0
function newWindow() {
count += 1;
window.open(url, 'testwindow' + count, 'width=640,height=480');
}
--></script>
</head>
<body>
<input type="button" value="New Window" onclick="newWindow()" />
<p>Test Link</p>
</body>
</html>"""
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
browser = Browser()
browser.setHtml(html)
browser.show()
sys.exit(app.exec_())
(NB: It is essential that a reference is kept to any newly created windows; failure to do so will almost certainly result in segmentation faults).
EDIT:
Just to clarify:
The createWindow function is intended to handle external requests to open a new browser window (e.g. a javascript window.open() call, as in the example above). And note that createWindow is a virtual function. This means that if it is overridden in a subclass of QWebView or QWebPage, the reimplemented function can be called internally by Qt. This is obviously vital if you need your own creatWindow function to be called from javascript code.
So if you want javascript programs on web-pages to be able to create and open an instance of your browser window class, then subclass QWebView and reimplement its createWindow function (as in the example above).
The createWindow function is not required at all for simply creating and showing a QWebView (e.g. adding a new tab). A QWebView is just a container for displaying web-pages, and is created and shown just like any other widget.
Related
I'm using wxpython (version 4.1.0 on Windows 10) grid to create a GUI and stumbled over this annoying bug:
If I select a cell and start the editing and in parallel hover with the mouse over other cells (without left-clicking), the editing will reset to an empty string continuously. It doesn't permanently lose focus, so if I continue writing, it will still edit the same cell. Here is a basic grid example to replicate this behavior:
"""Contains the main window of the Test view as well as the main function for the Test client."""
import wx
import wx.adv
import wx.grid as grid
from overrides import overrides
class TestFrame(wx.Frame):
"""The test frame."""
def __init__(self, parent: wx.Window, title: str) -> None:
super().__init__(parent, title=title, size=(1200, 600))
self.panel = TestPanel(self)
class TestPanel(wx.Panel):
"""The main panel of the Test window, containing all graphical elements."""
def __init__(self, parent: wx.Window) -> None:
super(TestPanel, self).__init__(parent)
self.grid = grid.Grid(self)
self.grid.CreateGrid(2, 11)
self.top_sizer = wx.BoxSizer(wx.VERTICAL)
self.top_sizer.Add(self.grid, 1, wx.ALL | wx.EXPAND)
self.SetSizer(self.top_sizer)
self.bottom_row_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.top_sizer.Add(self.bottom_row_sizer, 0, wx.EXPAND)
class TestApp(wx.App):
"""The Test app object."""
#overrides
def OnInit(self) -> bool:
"""Initializes the window."""
TestFrame(parent=None, title="Test").Show()
return True
def main() -> None:
"""Main control-flow entry function."""
TestApp().MainLoop()
if __name__ == "__main__":
main()
I'll probably have to override the BeginEdit or ApplyEdit method of the default wx.grid.GridCellEditor class, but I'm struggling with the actual solution.
I am super new to PyQT ... I am using QT Designer for my forms. I am confused about where to put my event handling for the second form in my project.
I have the following .py files :
main.py
Ui_make_render_folders.py (generated from the Qt designer ui file)
Ui_prefs.py (generated from the Qt designer ui file)
Mainwindow.py (this is where all the even handling for the main form
lives)
icons_rc.py (resource file)
make_render_folders.py (where all my custom functions live)
Here is a picture of the script running :
click to see script running << CLICK HERE
My question is ... where does the even handling for my prefs window go ???? I cant figure out where to put my [b]on_btn_released() for the buttons on y 2nd form.[/b] Also I am not sure I am instantiating that dialog properly. Currently I am adding this code to the top of my MainWindow.py.
Finally what does #pyqSignature("") do ? And do I need it ?
"""
This is where all teh MainWindow logic sits
"""
import os
import os.path
from PyQt4.QtGui import QMainWindow, QFileDialog, QTreeWidgetItem, QDialog
from PyQt4.QtCore import pyqtSignature, QString
from make_folders import make_render_folders
from Ui_make_render_folders import Ui_MainWindow
from Ui_prefs import Ui_prefs
class Prefs(QDialog, Ui_prefs):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setupUi(self)
#pyqtSignature("")
def on_btn_get_ref_dir_released(self):
print '2nd form'
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
"""
Constructor
"""
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.prefs = QDialog()
ui = Ui_prefs()
ui.setupUi(self.prefs)
#pyqtSignature("")
def on_btn_process_released(self):
print 'process'
no_of_shots = self.spn_no_of_shots.value()
#spot_path = self.le_proj_path.text()+'\\'+self.lst_spots.selectedItems()[0].text()
spot_path_string = 'self.le_proj_path.text())+os.sep,'
spot_path_string += str(self.lst_spots.selectedItems()[0].text())
os.path.join(spot_path_string)
save_position = self.lst_spots.selectedItems()[0]
if no_of_shots > 0:
if self.cmb_mode.currentText() == 'Create Shots':
print ('creating shots')
for x in range(1, no_of_shots+1):
make_render_folders.create_shot_folder(spot_path, x)
else:
print ('inserting shots')
if self.lst_shots.count() > 0:
t =self.lst_shots.selectedItems()[0].text()
cur_shot = t[2:]
cur_shot_val = int(cur_shot)
cur_index = self.lst_shots.currentRow()
print('sel_value :'+cur_shot)
print('sel_index :'+str(cur_index))
next_shot_index = int(cur_index)+1
print('nextshot_index ='+str(next_shot_index))
next_shot_text = self.lst_shots.item(next_shot_index).text()
next_shot_val = int(next_shot_text[2:])
print('nextshot value ='+str(next_shot_val))
insert_space = next_shot_val - cur_shot_val
print(str(insert_space))
if no_of_shots > (insert_space-1) :
print "not enough space - please reduce shots to insert"
else:
print('insert the shots')
for x in range(cur_shot_val,(cur_shot_val+no_of_shots) ):
print (str(x))
make_render_folders.insert_shot_folder(spot_path, x+1)
make_render_folders.populate_shot_list(self)
self.lst_shots.setCurrentRow(0)
#pyqtSignature("")
def on_btn_quit_released(self):
print 'quit'
self.close()
#pyqtSignature("")
def on_cmb_mode_currentIndexChanged(self, ret_text):
print ret_text
#pyqtSignature("")
def on_le_proj_path_editingFinished(self):
print "editingFinished le_proj_path"
def on_le_new_spot_name_textChanged(self):
if len(self.le_new_spot_name.text()) > 0 :
self.btn_add_spot.setEnabled(True)
else :
self.btn_add_spot.setEnabled(False)
#pyqtSignature("")
def on_btn_add_spot_released(self):
v_NewSpotFolder = self.le_new_spot_name.text()
v_rPath = self.le_proj_path.text()
x = make_render_folders.create_spot_folder(v_NewSpotFolder,v_rPath)
if x :
self.le_new_spot_name.clear()
make_render_folders.populate_spots_list(self)
#pyqtSignature("")
def on_actionLoad_Project_triggered(self):
print "actionLoad_Project"
self.lst_shots.clear()
self.lst_spots.clear()
proj_dir = str(QFileDialog.getExistingDirectory(self, 'Select Project'))
print proj_dir
if os.path.exists(proj_dir):
print 'the file is there'
elif os.access(os.path.dirname(proj_dir), os.W_OK):
print 'the file does not exists but write privileges are given'
else:
print 'can not write there'
p = os.path.join(proj_dir+os.sep,'renders')
self.le_proj_path.setText(p)
make_render_folders.populate_spots_list(self)
#pyqtSignature("")
def on_actionConfig_triggered(self):
print "config"
self.prefs.show()
#pyqtSignature("")
def on_actionQuit_triggered(self):
print "ActionQuit"
#pyqtSignature("")
def on_btn_get_folders_released(self):
make_render_folders.populate_spots_list(self)
def on_lst_spots_itemClicked (self):
print "got you"
self.lst_shots.clear()
make_render_folders.populate_shot_list(self)
Thanks !!!
Event handling for the Prefs window can go anywhere. Personally I always prefer to give each window its own file.
So the py files would look something like:
Mainwindow.py (Main form events etc)
Ui_make_render_folders.py (Qt Designer generated UI)
prefs.py (Prefs form events etc)
ui_prefs.py (Qt Designer generated UI)
your other files...
Initialisation: (MainWindow.py)
from PyQt4.QtGui import QMainWindow
from Ui_make_render_folders import Ui_MainWindow
#prefs here refers to the file: prefs.py. PrefWindow is the class within this file.
from prefs import PrefWindow
class MainWindow(QMainWindow):
def __init__(self, parent=None):
#Better way to initialise
super(MainWindow, self).__init__(parent)
self.main = Ui_MainWindow()
self.main.setupUi(self)
#initialise prefs to variable
self.prefs = PrefWindow(self)
# Rest of init...
Note: initialising of second form will be similar to above.
You should place all events for prefs form within its class.
From the function that calls Prefs:
#pyqtSlot()
def on_actionConfig_triggered(self):
print "config"
self.prefs.exec_() # Will block main window while its open.
# Use self.prefs.show() if you want user to be able to interact with main window
Finally: pyqtSignature
On http://pyqt.sourceforge.net/Docs/PyQt4/old_style_signals_slots.html it states that:
'The QtCore.pyqtSignature() serves the same purpose as the pyqtSlot() decorator but has a less Pythonic API.'
Better to use pyqtSlot()
I want only one instance of my app to be running at each time. but when the user attempts to open it the second time, I want the first window to be brought to the front (it could be just minimized or minimized to the corner of the taskbar and the user doesn't know how to open it)
I have this code that does the detection job and it doesn't allow the second instance. I have trouble with the part that it has to open the original window. I have commented out some of my attempt.
import sys
from PyQt4 import QtGui, QtCore
import sys
class SingleApplication(QtGui.QApplication):
def __init__(self, argv, key):
QtGui.QApplication.__init__(self, argv)
self._activationWindow=None
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey(key)
if self._memory.attach():
self._running = True
else:
self._running = False
if not self._memory.create(1):
raise RuntimeError(
self._memory.errorString().toLocal8Bit().data())
def isRunning(self):
return self._running
def activationWindow(self):
return self._activationWindow
def setActivationWindow(self, activationWindow):
self._activationWindow = activationWindow
def activateWindow(self):
if not self._activationWindow:
return
self._activationWindow.setWindowState(
self._activationWindow.windowState() & ~QtCore.Qt.WindowMinimized)
self._activationWindow.raise_()
self._activationWindow.activateWindow()
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.label = QtGui.QLabel(self)
self.label.setText("Hello")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.label)
if __name__ == '__main__':
key = 'random _ text'
app = SingleApplication(sys.argv, key)
if app.isRunning():
#app.activateWindow()
sys.exit(1)
window = Window()
#app.setActivationWindow(window)
#print app.topLevelWidgets()[0].winId()
window.show()
sys.exit(app.exec_())
I've made this work on Windows using the win32 api (I'm not entirely sure, but there are probably equivalent calls on macos/unix).
Add the following import to your application,
import win32gui
set the window title to a fixed name (instead of doing this, you could store its whndl in the shared memory)
window = Window()
window.setWindowTitle('Single Application Example')
window.show()
and then change your activateWindow method to something like the following:
def activateWindow(self):
# needs to look for a named window ...
whndl = win32gui.FindWindowEx(0, 0, None, "Single Application Example")
if whndl is 0:
return #couldn't find the name window ...
#this requests the window to come to the foreground
win32gui.SetForegroundWindow(whndl)
You might be interested by the solutions proposed here
For instance, I would try:
app = SingleApplication(sys.argv, key)
if app.isRunning():
window = app.activationWindow()
window.showNormal()
window.raise()
app.activateWindow()
sys.exit(1)
window = Window()
app.setActivationWindow(window)
window.setWindowFlags(Popup)
window.show()
I use GTK and Python for developing an application.
I want to load TreeView elements (1 column) from SQLite3 database.
But something go wrong (without any error)!
Here is a whole code:
#!/usr/bin/python
import sys
import sqlite3 as sqlite
from gi.repository import Gtk
from gi.repository import Notify
def notify(notifer, text, notificationtype=""):
Notify.init("Application")
notification = Notify.Notification.new (notifer, text, notificationtype)
notification.show ()
def get_object(gtkname):
builder = Gtk.Builder()
builder.add_from_file("main.ui")
return builder.get_object(gtkname)
def base_connect(basefile):
return sqlite.connect(basefile)
class Handler:
def main_destroy(self, *args):
Gtk.main_quit(*args)
def hardwaretool_clicked(self, widget):
baselist = get_object("subjectlist")
baselist.clear()
base = base_connect("subjectbase")
with base:
cur = base.cursor()
cur.execute("SELECT * FROM sub")
while True:
row = cur.fetchone()
if row == None:
break
iter = baselist.append()
print "row ", row[0]
baselist.set(iter, 0, row[0])
cur.close()
def gamestool_clicked(self, widget):
print("gamestool clicked!!!!! =)")
def appstool_clicked(self, widget):
print("appstool clicked!!!!! =)")
def fixtool_clicked(self, widget):
notify("Charmix","Fix Applied", "dialog-ok")
def brokenfixtool_clicked(self, widget):
notify("Charmix","Broken Fix Report Sended", "dialog-error")
def sendfixtool_clicked(self, widget):
notify("Charmix","Fix Sended", "dialog-information")
class CharmixMain:
def __init__(self):
builder = Gtk.Builder()
builder.add_from_file("main.ui")
self.window = builder.get_object("main")
self.subject = builder.get_object("subjectlist")
self.problem = builder.get_object("problemlist")
self.toolbar = builder.get_object("toolbar")
self.hardwaretool = builder.get_object("hardwaretool")
self.gamestool = builder.get_object("gamestool")
self.appstool = builder.get_object("appstool")
self.fixtool = builder.get_object("fixtool")
self.brokenfixtool = builder.get_object("brokenfixtool")
self.sendfixtool = builder.get_object("sendfixtool")
builder.connect_signals(Handler())
context = self.toolbar.get_style_context()
context.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
if __name__ == "__main__":
Charmix = CharmixMain()
Charmix.window.show()
Gtk.main()
I'm interested in this part (not working normally):
def hardwaretool_clicked(self, widget):
baselist = get_object("subjectlist")
baselist.clear()
base = base_connect("subjectbase")
with base:
cur = base.cursor()
cur.execute("SELECT * FROM sub")
while True:
row = cur.fetchone()
if row == None:
break
iter = baselist.append()
print "row ", row[0]
baselist.set(iter, 0, row[0])
cur.close()
TreeView(subjecttree) don't display anything, but print "row ", row[0] works fine and display all the strings.
Please, help me.
Maybe i need to repaint TreeView or thomething like that?
Do you know, how can I get it?
The problem is in your get_object method.
When you do:
builder = Gtk.Builder()
builder.add_from_file("main.ui")
you're actually creating a new window; even if you are using the same ui file, you are creating a completely different widget.
One way to get around the problem of accesing the widgets you need to process with your handler is to pass them as parameter of the constructor:
class Handler(object):
def __init__(self, widget1, widget2):
self.widget1 = widget1
self.widget2 = widget2
...
You can use those widgets on the handler's method afterwards.
Another way of accesing the widgets in a more 'decoupled' way is to add the object you want to use as the last parameter of the connect method when you're connecting signals; the drawback is that you would have to do this manually (since Glade doesn't provide this posibility)
self.widget.connect('some-signal', handler.handler_method, object_to_use)
I have an app with hundreds of custom buttons, each one needs multiple signal connections. The connect calls seem to be pretty slow, so I'm trying to connect/disconnect each button's signals via the main window's eventFilter using the enter and leave events.
However, sometimes those events seems to be called multiple times, causing RuntimeErrors (when trying to disconnect an event that is already gone).
Here is a snippet of code that shows a similar (and hopefully related) problem using default PushButtons.
To see the runtime error here, run the code, push one of the buttons, then close the window. That's when I see this:
RuntimeError: Fail to disconnect signal clicked().
Here is the code. Does anybody know if this is a PySide bug?
from PySide.QtGui import *
from PySide.QtCore import *
import sys
class TestWindow( QWidget ):
def __init__( self, parent=None ):
super( TestWindow, self ).__init__( parent )
self.setLayout( QGridLayout() )
def addWidget( self, w ):
self.layout().addWidget( w )
def testCB( self ):
print 'button connected'
def eventFilter( self, obj, event ):
'''Connect signals on mouse over'''
if event.type() == QEvent.Enter:
print 'enter',
obj.clicked.connect( self.testCB )
elif event.type() == QEvent.Leave:
print 'leave'
obj.clicked.disconnect( self.testCB )
return False
app = QApplication( sys.argv )
w = TestWindow()
for i in xrange(10):
btn = QPushButton( 'test %s' % i )
w.addWidget( btn )
btn.installEventFilter(w)
w.show()
sys.exit( app.exec_() )
In few cases, when I tested mouse events, showed better performance while events are attached to item class... so don't subclass. Rather :
class Button(QPushButton):
def __init__(self, label):
super(Button, self).__init__()
self.setText(label)
app = QApplication( sys.argv )
w = TestWindow()
for i in xrange(10):
btn = Button( 'test %s' % i )
w.addWidget( btn )
...then define mouse event for class.