Environment:
Windows 10 Home Edition host
VirtualBox running Ubuntu 22.04 Desktop
In previous versions of Ubuntu installed gnome-session-flashback and have been able to edit menu including adding new menu items
Upon fresh installation of Ubuntu 22.04
Items could be removed/moved within a category &etc.
However "New Item" would bring up dialog box but would not process item to add.
Gnome 42 Alacarte Error AttributeError: 'LauncherEditor' object has no attribute 'icon_file'
This error just indicates that you need to choose an icon file image for the command and it will save Menu Item with no error you do not need to change the code to prior version .
Ran menu edit in terminal with privileges:
$sudo alacarte
watching output/stack trace upon submitting 'add item' results:
AttributeError: 'LauncherEditor' object has no attribute 'icon_file'
Checked file/line # in stack trace. (file: usr/share/alacarte/Alacarte/ItemEditor.py) Compared this to working version in Ubuntu 20.04
lines of code: 20.04 --> 269 vs 22.04 --> 293
Backed up 22.04 version of ItemEditor.py
Replaced it with 20.04 version of ItemEditor.py
Menu edits including add item working.
Here is the ItemEditor.py 20.04 version:
# -*- coding: utf-8 -*-
# Alacarte Menu Editor - Simple fd.o Compliant Menu Editor
# Copyright (C) 2013 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import gettext
import os
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, GObject, Gtk
from Alacarte import config, util
_ = gettext.gettext
EXTENSIONS = (".png", ".xpm", ".svg")
def try_icon_name(filename):
# Detect if the user picked an icon, and make
# it into an icon name.
if not filename.endswith(EXTENSIONS):
return filename
theme = Gtk.IconTheme.get_default()
resolved_path = None
for path in theme.get_search_path():
if filename.startswith(path):
resolved_path = filename[len(path):].lstrip(os.sep)
break
if resolved_path is None:
return filename
parts = resolved_path.split(os.sep)
# icon-theme/size/category/icon
if len(parts) != 4:
return filename
icon_name = parts[3]
# strip extension
return icon_name[:-4]
def get_icon_string(image):
filename = image.props.file
if filename is not None:
return try_icon_name(filename)
return image.props.icon_name
def strip_extensions(icon):
if icon.endswith(EXTENSIONS):
return icon[:-4]
else:
return icon
def set_icon_string(image, icon):
if GLib.path_is_absolute(icon):
image.props.file = icon
else:
image.props.icon_name = strip_extensions(icon)
DESKTOP_GROUP = GLib.KEY_FILE_DESKTOP_GROUP
# XXX - replace with a better UI eventually
class IconPicker(object):
def __init__(self, dialog, button, image):
self.dialog = dialog
self.button = button
self.button.connect('clicked', self.pick_icon)
self.image = image
def pick_icon(self, button):
chooser = Gtk.FileChooserDialog(title=_("Choose an icon"),
parent=self.dialog,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
response = chooser.run()
if response == Gtk.ResponseType.ACCEPT:
self.image.props.file = chooser.get_filename()
chooser.destroy()
class ItemEditor(GObject.GObject):
ui_file = None
__gsignals__ = {
'response': (GObject.SIGNAL_RUN_FIRST, None, (bool,))
}
def __init__(self, parent, item_path):
GObject.GObject.__init__(self)
self.builder = Gtk.Builder()
self.builder.add_from_file(os.path.join(config.pkgdatadir, self.ui_file))
self.dialog = self.builder.get_object('editor')
self.dialog.set_transient_for(parent)
self.dialog.connect('response', self.on_response)
self.build_ui()
self.item_path = item_path
self.load()
self.resync_validity()
def build_ui(self):
raise NotImplementedError()
def get_keyfile_edits(self):
raise NotImplementedError()
def set_text(self, ctl, name):
try:
val = self.keyfile.get_string(DESKTOP_GROUP, name)
except GLib.GError:
pass
else:
self.builder.get_object(ctl).set_text(val)
def set_check(self, ctl, name):
try:
val = self.keyfile.get_boolean(DESKTOP_GROUP, name)
except GLib.GError:
pass
else:
self.builder.get_object(ctl).set_active(val)
def set_icon(self, ctl, name):
try:
val = self.keyfile.get_string(DESKTOP_GROUP, name)
except GLib.GError:
pass
else:
set_icon_string(self.builder.get_object(ctl), val)
def load(self):
self.keyfile = GLib.KeyFile()
try:
self.keyfile.load_from_file(self.item_path, util.KEY_FILE_FLAGS)
except GLib.GError:
pass
def save(self):
util.fillKeyFile(self.keyfile, self.get_keyfile_edits())
contents, length = self.keyfile.to_data()
with open(self.item_path, 'w') as f:
f.write(contents)
def run(self):
self.dialog.present()
def on_response(self, dialog, response):
if response == Gtk.ResponseType.OK:
self.save()
self.dialog.destroy()
self.emit('response', response == Gtk.ResponseType.OK)
class LauncherEditor(ItemEditor):
ui_file = 'launcher-editor.ui'
def build_ui(self):
self.icon_picker = IconPicker(self.dialog,
self.builder.get_object('icon-button'),
self.builder.get_object('icon-image'))
self.builder.get_object('exec-browse').connect('clicked', self.pick_exec)
self.builder.get_object('name-entry').connect('changed', self.resync_validity)
self.builder.get_object('exec-entry').connect('changed', self.resync_validity)
def exec_line_is_valid(self, exec_text):
try:
success, parsed = GLib.shell_parse_argv(exec_text)
# Make sure program (first part of the command) is in the path
command = parsed[0]
return (GLib.find_program_in_path(command) is not None)
except GLib.GError:
return False
def resync_validity(self, *args):
name_text = self.builder.get_object('name-entry').get_text()
exec_text = self.builder.get_object('exec-entry').get_text()
valid = (name_text != "" and self.exec_line_is_valid(exec_text))
self.builder.get_object('ok').set_sensitive(valid)
def load(self):
super(LauncherEditor, self).load()
self.set_text('name-entry', "Name")
self.set_text('exec-entry', "Exec")
self.set_text('comment-entry', "Comment")
self.set_check('terminal-check', "Terminal")
self.set_icon('icon-image', "Icon")
def get_keyfile_edits(self):
return dict(Name=self.builder.get_object('name-entry').get_text(),
Exec=self.builder.get_object('exec-entry').get_text(),
Comment=self.builder.get_object('comment-entry').get_text(),
Terminal=self.builder.get_object('terminal-check').get_active(),
Icon=get_icon_string(self.builder.get_object('icon-image')),
Type="Application")
def pick_exec(self, button):
chooser = Gtk.FileChooserDialog(title=_("Choose a command"),
parent=self.dialog,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
response = chooser.run()
if response == Gtk.ResponseType.ACCEPT:
self.builder.get_object('exec-entry').set_text(chooser.get_filename())
chooser.destroy()
class DirectoryEditor(ItemEditor):
ui_file = 'directory-editor.ui'
def build_ui(self):
self.icon_picker = IconPicker(self.dialog,
self.builder.get_object('icon-button'),
self.builder.get_object('icon-image'))
self.builder.get_object('name-entry').connect('changed', self.resync_validity)
def resync_validity(self, *args):
name_text = self.builder.get_object('name-entry').get_text()
valid = (name_text != "")
self.builder.get_object('ok').set_sensitive(valid)
def load(self):
super(DirectoryEditor, self).load()
self.set_text('name-entry', "Name")
self.set_text('comment-entry', "Comment")
self.set_icon('icon-image', "Icon")
def get_keyfile_edits(self):
return dict(Name=self.builder.get_object('name-entry').get_text(),
Comment=self.builder.get_object('comment-entry').get_text(),
Icon=get_icon_string(self.builder.get_object('icon-image')),
Type="Directory")
def test_editor(path):
if path.endswith('.directory'):
return DirectoryEditor(path)
elif path.endswith('.desktop'):
return LauncherEditor(path)
else:
raise ValueError("Invalid filename, %r" % (path,))
def test():
import sys
Gtk.Window.set_default_icon_name('alacarte')
editor = test_editor(sys.argv[1])
editor.dialog.connect('destroy', Gtk.main_quit)
editor.run()
Gtk.main()
if __name__ == "__main__":
test()
I have a Qt application with an embedded Jupyter QtConsole. This is the code that I use to create the Jupyter console:
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
class EmbeddedJupyterConsole(RichJupyterWidget):
def __init__(self, gui):
super(RichJupyterWidget, self).__init__(gui.ipython_container)
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel = self.kernel_manager.kernel
self.kernel.gui = 'qt4'
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
gui.ipython.kernel_manager = self.kernel_manager
gui.ipython.kernel_client = self.kernel_client
gui.ipython.exit_requested.connect(self.stop)
def executeCmd(self, cmd):
self.kernel.shell.ev(cmd)
def pushVar(self, **kwarg):
self.kernel.shell.push(kwarg)
def stop(self):
self.kernel_client.stop_channels()
self.kernel_manager.shutdown_kernel()
self.app.exit()
And this is the code that I use to generate the main Qt window:
class QtMainWindow(QMainWindow):
def __init__(self,app):
self.console = EmbeddedJupyterConsole(self)
# Initialize other widgets within the main window
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = QtMainWindow(app)
sys.exit(app.exec_())
Whenever I run this code, the Jupyter console and QtApplication work correctly. However, I also see the following error:
Traceback (most recent call last):
File "C:\...\Miniconda2\lib\site-packages\qtconsole\console_widget.py", line 464, in sizeHint
font_metrics = QtGui.QFontMetrics(self.font)
AttributeError: 'EmbeddedJupyterConsole' object has no attribute 'font'
If I add add a "fake" font property to the EmbeddedJupyterConsole, then I loose the nice format of the console and the error still appears. Does anyone know what is the issue and how to solve it?
I have connected a QPushButton to a method that call file dialog. The simplified code look like this:
def init_buttons(self):
self.browse_button = QPushButton('&Browse')
self.browse_button.clicked.connect(self.browse_file)
def browse_file(self):
file_name = QFileDialog.getExistingDirectory()
# Just for checking
print(file_name)
Sometimes QFileDialog won't showing up. The process is indeed running, since the main class/widget doesn't response to my clicking. Sometimes it's showing up.
If QFileDialog doesn't show up, with pycharm, I have to stop and kill process to end the program. If I run the program directly from terminal, I have to manually end the running process to end the program. I can't figure out what causing this, since terminal not showing any exception or warning.
So, what is this?
The parameters for the getExistingDirectory were wrong. Please try this. Also, I have added further information in my pull request.
import os
def browse_file(self):
self.save_dir = QFileDialog.getExistingDirectory(self,
"Open Save Directory", os.path.expanduser('~'))
print(self.save_dir)
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import (QMainWindow, QTextEdit,
QAction,QMessageBox, QFileDialog, QApplication,QPushButton,QInputDialog,QLineEdit)
from PyQt5.QtGui import QIcon
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.fileName=""
self.text=""
btn1 = QPushButton("Encrypt", self)
btn1.clicked.connect(self.onBtn1)
self.show()
def onBtn1(self):
self.fileName, _ = QFileDialog.getOpenFileName(self, 'Open file', '/Users/Jarvis/Desktop/')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I am trying to download the content of both the links.The first link works properly and the content is downloaded as a html file.But the content of the 2 nd link(a text file) is not downloaded.Please help me out.
Here is my code :
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
import sys
import codecs
class Downloader(QObject):
# To be emitted when every items are downloaded
done = Signal()
def __init__(self, urlList, parent = None):
super(Downloader, self).__init__(parent)
self.urlList = urlList
self.counter = 0
# As you probably don't need to display the page
# you can use QWebPage instead of QWebView
self.page = QWebPage(self)
self.page.loadFinished.connect(self.save)
self.startNext()
def currentUrl(self):
return self.urlList[self.counter][0]
def currentFilename(self):
return self.urlList[self.counter][1]
def startNext(self):
print "Downloading %s..."%self.currentUrl()
self.page.mainFrame().load(self.currentUrl())
def save(self, ok):
if ok:
data = self.page.mainFrame().toHtml()
with codecs.open(self.currentFilename(), encoding="utf-8", mode="w") as f:
f.write(data)
print "Saving %s to %s."%(self.currentUrl(), self.currentFilename())
else:
print "Error while downloading %s\nSkipping."%self.currentUrl()
self.counter += 1
if self.counter < len(self.urlList):
self.startNext()
else:
self.done.emit()
urlList = [("http://www.nsf.gov/awardsearch/simpleSearchResult?queryText=8","nsf.html"),
("http://www.nsf.gov/awardsearch/ExportResultServlet?exportType=txt","a.txt")]
app = QApplication(sys.argv)
downloader = Downloader(urlList)
# Quit when done
downloader.done.connect(app.quit)
# To view the pages
web = QWebView()
# To prevent user action that would interrupt the current page loading
web.setDisabled(True)
web.setPage(downloader.page)
web.show()
sys.exit(app.exec_())
I haven't quite found the answer yet in other threads with similar titles. Let's say I have a logging.conf that looks like this:
[loggers]
keys=root,analyzer
[handlers]
keys=consoleHandler,analyzerFileHandler
[formatters]
keys=consoleFormatter,logFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler,analyzerFileHandler
[logger_analyzer]
level=DEBUG
handlers=consoleHandler,analyzerFileHandler
qualname=analyzer
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=consoleFormatter
args=(sys.stdout,)
[handler_analyzerFileHandler]
class=FileHandler
level=DEBUG
formatter=logFormatter
args=('analyzer.log','w')
[formatter_consoleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s | %(message)s
datefmt=%m/%d/%Y %X
[formatter_logFormatter]
format=%(asctime)s - %(levelname)s | %(message)s
datefmt=%m/%d/%Y %X
A logger = logging.getLogger('analyzer') will send text to the log file and the console, how can I make that sys.stdout go towards a QPlainTextEdit() widget instead of the console?
edit
OK looking at this post I made this code. The post's code is good, but for some reason it doesn't address the issue, you can comment out all the instances of logger and still end up with print events going to Qwidget. The logger, as is, has no real interaction with the rest of the program, it is just there. I thought I could rectify the problem by writing a class that took whatever I wanted as text and sent it off to print and logger respectively:
import logging
from logging.config import fileConfig
from os import getcwd
import sys
from PyQt4.QtCore import QObject,\
pyqtSignal
from PyQt4.QtGui import QDialog, \
QVBoxLayout, \
QPushButton, \
QTextBrowser,\
QApplication
class XStream(QObject):
_stdout = None
_stderr = None
messageWritten = pyqtSignal(str)
def flush( self ):
pass
def fileno( self ):
return -1
def write( self, msg ):
if ( not self.signalsBlocked() ):
self.messageWritten.emit(unicode(msg))
#staticmethod
def stdout():
if ( not XStream._stdout ):
XStream._stdout = XStream()
sys.stdout = XStream._stdout
return XStream._stdout
#staticmethod
def stderr():
if ( not XStream._stderr ):
XStream._stderr = XStream()
sys.stderr = XStream._stderr
return XStream._stderr
class XLogger():
def __init__(self, name):
self.logger = logging.getLogger(name)
def debug(self,text):
print text
self.logger.debug(text)
def info(self,text):
print text
self.logger.info(text)
def warning(self,text):
print text
self.logger.warning(text)
def error(self,text):
print text
self.logger.error(text)
class MyDialog(QDialog):
def __init__( self, parent = None ):
super(MyDialog, self).__init__(parent)
# setup the ui
self._console = QTextBrowser(self)
self._button = QPushButton(self)
self._button.setText('Test Me')
# create the layout
layout = QVBoxLayout()
layout.addWidget(self._console)
layout.addWidget(self._button)
self.setLayout(layout)
# create connections
XStream.stdout().messageWritten.connect( self._console.insertPlainText )
XStream.stderr().messageWritten.connect( self._console.insertPlainText )
self.xlogger = XLogger('analyzer')
self._button.clicked.connect(self.test)
def test( self ):
# log some stuff
self.xlogger.debug("Testing debug")
self.xlogger.info('Testing info')
self.xlogger.warning('Testing warning')
self.xlogger.error('Testing error')
if ( __name__ == '__main__' ):
fileConfig(''.join([getcwd(),'/logging.conf']))
app = None
if ( not QApplication.instance() ):
app = QApplication([])
dlg = MyDialog()
dlg.show()
The "cross logger" sends everything to the log and to the Qwidget, however it also sends everything but debug to the aptana console:
02/05/2013 17:38:42 - analyzer - INFO | Testing info
02/05/2013 17:38:42 - analyzer - WARNING | Testing warning
02/05/2013 17:38:42 - analyzer - ERROR | Testing error
While the analyzer.log has:
02/05/2013 17:38:42 - DEBUG | Testing debug
02/05/2013 17:38:42 - INFO | Testing info
02/05/2013 17:38:42 - WARNING | Testing warning
02/05/2013 17:38:42 - ERROR | Testing error
Weird that debug is the only one who doesn't make it to the Aptana console, removing consoleHandler from handlers under [logger_analyzer] in my logging.conf solves the problem of it writing out to the Aptana console, probably has something to do with the args=(sys.stdout,) under [handler_consoleHandler]. I suppose it solves my problem without having to code in a handler for the Qtext object and thus negate the logging.conf file. If someone has a more elegant solution to using a logging.conf file that somehow manages to redirect its console output to a Qwidget of your choice, please feel free to post. Thanks.