Ubuntu 22.04 installed gnome-session-flashback not able to add new menu item - ubuntu-22.04

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()

Related

How to log user activity in a streamlit app?

I have a streamlit app that is public (ie. no user log-in). I would like to have log files of the form:
|2023-02-10 16:30:16 : user at ip=___ clicked button key=___
|2023-02-10 16:30:19 : user at ip=___ clicked button key=___
|2023-02-10 16:31:10 : user at ip=___ clicked button key=___
|2023-02-10 16:31:27 : user at ip=___ clicked button key=___
|...
Is there any way to achieve this? It's because I want to do some analytics on how the app is being used.
You can access the remote ip address via get_script_run_ctx and .remote_ip:
from streamlit import runtime
from streamlit.runtime.scriptrunner import get_script_run_ctx
def get_remote_ip() -> str:
"""Get remote ip."""
try:
ctx = get_script_run_ctx()
if ctx is None:
return None
session_info = runtime.get_instance().get_client(ctx.session_id)
if session_info is None:
return None
except Exception as e:
return None
return session_info.request.remote_ip
import streamlit as st
st.title("Title")
st.markdown(f"The remote ip is {get_remote_ip()}")
For the logging part, I suggest you use a ContextFilter:
import logging
class ContextFilter(logging.Filter):
def filter(self, record):
record.user_ip = get_remote_ip()
return super().filter(record)
This custom filter will modify the LogRecord and add it the custom attribute user_ip that you can then use inside the Formatter.
All together, it gives:
import logging
import streamlit as st
from streamlit import runtime
from streamlit.runtime.scriptrunner import get_script_run_ctx
def get_remote_ip() -> str:
"""Get remote ip."""
try:
ctx = get_script_run_ctx()
if ctx is None:
return None
session_info = runtime.get_instance().get_client(ctx.session_id)
if session_info is None:
return None
except Exception as e:
return None
return session_info.request.remote_ip
class ContextFilter(logging.Filter):
def filter(self, record):
record.user_ip = get_remote_ip()
return super().filter(record)
def init_logging():
# Make sure to instanciate the logger only once
# otherwise, it will create a StreamHandler at every run
# and duplicate the messages
# create a custom logger
logger = logging.getLogger("foobar")
if logger.handlers: # logger is already setup, don't setup again
return
logger.propagate = False
logger.setLevel(logging.INFO)
# in the formatter, use the variable "user_ip"
formatter = logging.Formatter("%(name)s %(asctime)s %(levelname)s [user_ip=%(user_ip)s] - %(message)s")
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
handler.addFilter(ContextFilter())
handler.setFormatter(formatter)
logger.addHandler(handler)
def main():
logger.info("Inside main")
st.title("Title")
text = st.sidebar.text_input("Text:")
logger.info(f"This is the text: {text}")
if __name__ == "__main__":
init_logging()
logger = logging.getLogger("foobar")
main()
foobar 2023-02-13 15:43:57,252 INFO [user_ip=::1] - Inside main
foobar 2023-02-13 15:43:57,253 INFO [user_ip=::1] - This is the text: Hello, world!
Note: Here the user_ip is "::1" because everything was done locally.

Determine whether static file site contains css inheritance rule

I'm working on a site that renders static html files, and I wish to determine which pages in the site contain a specific css inheritance rule, such as .parent .child (a child class that descends from a parent).
I can imagine a web crawler that accesses each of those pages, runs a test to see whether the given page has that style, and returns a report, but are there any tools that already perform this work well for static file sites (e.g. not the css-tree-shake-plugin for webpack)? I'd be grateful for any insights others can offer on this question.
Here's what I came up with:
#!/usr/bin/env python
'''
from source:
pip install selenium
pip install beautifulsoup4
brew install phantomjs
usage: python shake_trees.py '_site' '.parent .child'
'''
from bs4 import BeautifulSoup
from selenium import webdriver
import sys, copy, multiprocessing, os, fnmatch
def clean_href(href):
return href.split('#')[0].split('?')[0]
def get_web_urls(all_links, visited):
recurse = False
for link in copy.copy(all_links):
if link not in visited:
visited.add(link)
driver.get(link)
for tag in driver.find_elements_by_tag_name('a'):
href = clean_href(tag.get_attribute('href'))
if domain in href and href not in visited:
recurse = True
all_links.add(href)
if recurse:
return get_web_urls(all_links, visited)
else:
print(all_links, visited)
return all_links
def get_static_site_urls():
matches = []
for root, dirnames, filenames in os.walk(root_url):
for filename in fnmatch.filter(filenames, file_match):
matches.append(os.path.join(root, filename))
return matches
if __name__ == '__main__':
# parse command line arguments
root_url = sys.argv[1]
css_selector = sys.argv[2]
# globals
domain = root_url
static_site = False if 'http' in root_url else True
file_match = '*.html'
# initialize the phantom driver
driver = webdriver.PhantomJS()
driver.set_window_size(1000, 1000)
if static_site:
urls = get_static_site_urls()
else:
driver.get( root_url )
urls = get_web_urls( set([root_url]), set() )
for url in urls:
if static_site:
url = 'file://' + os.path.join(os.getcwd(), url)
driver.get(url)
soup = BeautifulSoup( driver.page_source, 'html.parser' )
if soup.select_one(css_selector):
print('match on', url)

PyQt4 display dialog box on all displays windows 10

First off thanks for the help, I am a unix admin and don't know much about QT or windows 10
I have to display a qt popup menu/app on all displays on windows 10 using winexe to access a remote box, the behavior should be like the below winexe command:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'msg * hello jello'
below is my command line for the PyQt4 script:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'cmd /c c:\python26\python.exe c:\scripts\bobo3.py'
It just sits there and does nothing visible. My guess is it needs to be displayed to all active displays and I have no idea how to do this.
Here is the code, it works when run from a cmd prompt locally:
from optparse import OptionParser
from PyQt4 import QtGui, QtCore
class TopDialog(QtGui.QWidget):
def __init__(self, title='Message', width=960, height=640, ):
super(TopDialog, self).__init__()
self.setFixedSize(width, height)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
screen = QtGui.QDesktopWidget().screenGeometry()
self.move((screen.width() - self.width())/2, (screen.height() - self.height())/2)
if not isinstance(title, unicode):
title = title.decode('utf-8')
self.title_lb = QtGui.QLabel(title)
self.title_lb.setAlignment(QtCore.Qt.AlignCenter)
self.title_lb.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken)
self.message_lb = QtGui.QLabel()
self.message_lb.setAlignment(QtCore.Qt.AlignCenter)
self.message_lb.setMinimumSize(self.width()/2, self.height()/2)
self.ok_bt = QtGui.QPushButton('OK')
self.ok_bt.clicked.connect(self._ok_bt_clicked)
self.main_lo = QtGui.QVBoxLayout()
self.main_lo.addWidget(self.title_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.message_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.ok_bt)
self.setLayout(self.main_lo)
def set_message(self, message):
if not isinstance(message, unicode):
message = message.decode('utf-8')
self.message_lb.setText(message)
def set_button_text(self, text):
if not isinstance(text, unicode):
text = text.decode('utf-8')
self.ok_bt.setText(text)
def _ok_bt_clicked(self):
# do something you want.
self.close() # close the window
client = None
def main():
global client
parser = OptionParser()
parser.add_option('-m', '--message', type=str, action="store", dest="message", help="The alert message")
parser.add_option('-s', '--sent_by', type=str, action="store", dest="sent_by", help='login of sender')
(options, args) = parser.parse_args()
app = QtGui.QApplication(sys.argv)
sent_by = options.sent_by
if sent_by is None:
sent_by = "Someone"
message = options.message
if message is None:
message = "we are not sure why"
print sent_by, message
client = TopDialog(' %s wants your attention' % (sent_by))
client.set_message('%s' % (message))
client.set_button_text('OK')
client.show()
app.exec_()
main()

Python QtWebKit 2nd link not downloaded

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_())

Python logging config that writes to Qt widget

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.

Resources