The Perfect BibleAnalyzer Experience on Any Distro
-
arcanemuse
- Posts: 6
- Joined: Sat Dec 31, 2022 5:13 pm
Re: The Perfect BibleAnalyzer Experience on Any Distro
I tried to use this on Mandriva. I got to the distrobox create line. First error it was complaining about crun. I installed that. Now it is saying a mount_program is required. 'overlay' is not supported over overlayfs. I have no idea what this means. I am an idiot so I can't fix it. I got as far as it being an issue with fuse-overlayfs which you can't get for Mandriva. I'm glad I'm trying this on a live usb and not an actual install. I always try to get bible analyzer working on a live usb first before I install. What's the point of installing the distro if the program won't work on it? I'm sort of stuck on Mint and MxLinux for the most part. Well, I'll be stuck on Mint only when they update to Debian 13 on MXLinux. BA won't run on Debian 13. I've tried a live usb already and actually updated a 12 install by mistake. I had to wipe my disk and reinstall everything to get Debian 12 back. Better to have a 2 year old distro that actually allows BA to run than a new one that won't run it. I have one laptop running Arch and I hate it but BA is in the user repos. You can just install the latest version. Every other machine will have to be on an old version of Debian or MX or Mint I guess. I won't use Ubuntu with their spyware and snap garbage. Your options as to a linux distro are pretty limited when the program you need only comes in .deb format. I was so hoping that this would actually work. I'd like to switch totally to Linux but I have a lot invested in Logos and Accordance and need either Mac or Windoze to run those. I don't have 2500 just lying around to buy a Mac so that means being stuck on Windoze.
-
arcanemuse
- Posts: 6
- Joined: Sat Dec 31, 2022 5:13 pm
Re: The Perfect BibleAnalyzer Experience on Any Distro
OK. I have a really thick head and I'm stubborn as a mule. I struck out on Mandriva but tried again on a live USB of Fedora 42 KDE. It took me a minute to find the shortcut in lost and found in the application menu but there it be. It works. Flawlessly. Thank you. What can I say? Mandriva is garbage apparently. Fedora did the job with no hassle. It simply worked when I put in the commands. I think I will be installing it as a keeper on this laptop. I've heard good things and it deserves a good spin around the block and a severe tire kicking. It has been years since I have had anything to do with an rpm based system. My last experience wasn't so good but that was a long time ago. I'm sure they have improved things over time.
-
wmcdannell
- Posts: 37
- Joined: Thu Oct 12, 2023 5:13 pm
Re: The Perfect BibleAnalyzer Experience on Any Distro
Tested and works fine on Ubuntu 25.10 (CachyOS [Arch] host). Note that if you have access to the AUR (Arch User Repository) there is a PKGBUILD available for Bibleanaylzer (it also works fine on CachyOS/Arch).
Supported tags on Docker hub are listed here:
https://hub.docker.com/_/ubuntu
For 25.10 the install command is:
If you use a dark theme and it doesn't work well with BA you can change the theme using the following when launching /usr/bin/bibleanalyzer or /opt/bibleanalyzer/ba-run-py:
It can be placed in /usr/bin/bibleanalyzer:
Or when you update the .desktop file:
The following is a UX hack for those that are interested and to keep a note for myself. It completely disables the autocomplete popups for input boxes because they don't work correctly on Linux platforms.
You'll need an editor installed to edit the following files so or whichever editor you prefer. Change line 8 in /opt/bibleanalyzer/ba-run.py to the following (you may need to use sudo to edit the file):
If you don't want to use sudo to edit the files in /opt/bibleanalyzer you can change the owner for the entire path
Save, and close the file and then run the following (don't use sudo if you change the owner of the path):
Then, replace /opt/bibleanalyzer/ba-565/autocomplete.py with the following (it will automatically detect when running on Linux and disable the autocomplete popups app-wide otherwise it's exactly the same as the original; note that the indentations/tabs are critical):
Code: Select all
distrobox create --image ubuntu:25.10 --name bibleanalyzer --home ~/.bibleanalyzercontainer/https://hub.docker.com/_/ubuntu
For 25.10 the install command is:
Code: Select all
sudo apt install ./bibleanalyzer_5.6-1_all.deb python3-six -yCode: Select all
env GTK_THEME=Adwaita bibleanalyzerCode: Select all
env GTK_THEME=Adwaita python3 /opt/bibleanalyzer/ba-run.pyCode: Select all
env XDG_DATA_DIRS=/usr/share/ubuntu:/usr/share/gnome:/usr/local/share:/usr/share GTK_THEME=Adwaita python3 -u /opt/bibleanalyzer/ba-run.py"The following is a UX hack for those that are interested and to keep a note for myself. It completely disables the autocomplete popups for input boxes because they don't work correctly on Linux platforms.
You'll need an editor installed to edit the following files so
Code: Select all
sudo apt install -y nanoCode: Select all
sys.path.insert(0, '/opt/bibleanalyzer/ba-565')
Code: Select all
chown -R $USER:$USER /opt/bibleanalyzer
Code: Select all
sudo unzip /opt/bibleanalyzer/ba-565.zip -d /opt/bibleanalyzer/ba-565Code: Select all
# -*- coding: utf-8 -*-
__license__ = """Copyright (c) 2008-2010, Toni Ruža, All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE."""
__author__ = u"Toni Ruža <gmr.gaf@gmail.com>"
__url__ = "http://bitbucket.org/raz/wxautocompletectrl"
import wx
import wx.html
DISABLE_AUTOCOMPLETE = wx.Platform == "__WXGTK__"
class DisabledSuggestionsPopup(object):
Shown = False
def __init__(self):
self._suggestions = None
self._unformated_suggestions = None
self.inFilter = False
def __bool__(self):
return True
__nonzero__ = __bool__
def __getattr__(self, name):
if name == "Position":
return None
raise AttributeError(name)
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
def Hide(self):
pass
def DoHide(self):
pass
def DoShow(self):
pass
def ShowWithoutActivating(self):
pass
def IsShown(self):
return False
def IsActive(self):
return False
def SetSize(self, size):
pass
def SetSuggestions(self, suggestions, unformated_suggestions):
pass
def CursorUp(self):
pass
def CursorDown(self):
pass
def CursorHome(self):
pass
def CursorEnd(self):
pass
def GetSelectedSuggestion(self):
return None
def GetSuggestion(self, n):
return None
class SuggestionsPopup(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, style=wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP|wx.BORDER_NONE)
self._suggestions = self._listbox(self)
self._suggestions.SetItemCount(0)
self._unformated_suggestions = None
self._suggestions.SetSelectionBackground(wx.Colour(100, 100, 200))
self.mainApp = wx.GetApp().GetTopWindow()
self.inFilter = False
class _listbox(wx.html.HtmlListBox):
items = None
def OnGetItem(self, n):
return self.items[n]
def OnDrawBackground(self, dc, rect, item):
if item % 2 == 0:
bgCol = wx.Colour(240,240,255)
dc.SetBrush(wx.Brush(bgCol))
dc.SetPen(wx.Pen(bgCol))
dc.DrawRectangle(rect)
def SetSuggestions(self, suggestions, unformated_suggestions):
self._suggestions.items = suggestions
self._suggestions.SetItemCount(len(suggestions))
self._suggestions.SetSelection(0)
self._suggestions.Refresh()
self._unformated_suggestions = unformated_suggestions
def CursorUp(self):
selection = self._suggestions.GetSelection()
if selection > 0:
self._suggestions.SetSelection(selection - 1)
def CursorDown(self):
selection = self._suggestions.GetSelection()
last = self._suggestions.GetItemCount() - 1
if selection < last:
self._suggestions.SetSelection(selection + 1)
def CursorHome(self):
if self.IsShown():
self._suggestions.SetSelection(0)
def CursorEnd(self):
if self.IsShown():
self._suggestions.SetSelection(self._suggestions.GetItemCount() - 1)
def GetSelectedSuggestion(self):
#if self.inFilter:
return self._unformated_suggestions[self._suggestions.GetSelection()]
'''
else:
print 'else'
if self.mainApp.dctNB.dctPanel.ac.HasFocus():
print 'in dct', self._suggestions.GetSelection()
return self.mainApp.currentWL[self.mainApp.dctNB.dctPanel.ac.popup._suggestions.GetSelection()]
elif self.mainApp.cmtNB.cmtPanel.ac.HasFocus():
return self.mainApp.currentVL[self.mainApp.cmtNB.cmtPanel.ac.popup._suggestions.GetSelection()]
elif self.mainApp.noteEdit.ac.HasFocus():
return self.mainApp.currentVL[self.mainApp.noteEdit.ac.popup._suggestions.GetSelection()]
'''
def GetSuggestion(self, n):
#if self.inFilter:
return self._unformated_suggestions[n]
'''
else:
if self.mainApp.dctNB.dctPanel.ac.popup._suggestions.HasFocus():
return self.mainApp.currentWL[n]
elif self.mainApp.cmtNB.cmtPanel.ac.popup._suggestions.HasFocus() or \
self.mainApp.noteEdit.ac.popup._suggestions.HasFocus():
return self.mainApp.currentVL[n]
'''
def DoHide(self):
self.Hide()
def DoShow(self):
self.ShowWithoutActivating()
class AutocompleteTextCtrl(wx.ComboCtrl):
def __init__(self, parent, size=(160,-1), height=350, completer=None, multiline=False, frequency=25):
self.mainApp = wx.GetApp().GetTopWindow()
self.noFocusSel = 0
style = wx.TE_PROCESS_ENTER
if multiline:
style = style | wx.TE_MULTILINE
wx.ComboCtrl.__init__(self, parent, size=size, style=style)
self.height = height
self.frequency = frequency
if completer:
self.SetCompleter(completer)
self.queued_popup = False
self.skip_event = False
self.parent = parent.GetParent()
def DoSetPopupControl(self, popup):
pass
def OnButtonClick(self):
if DISABLE_AUTOCOMPLETE:
return
if self.parent == self.mainApp.dctNB:
self.mainApp.dctNB.dctPanel.OnDown()
elif self.parent == self.mainApp.cmtNB:
self.mainApp.cmtNB.cmtPanel.OnDown()
else:
self.mainApp.noteEdit.OnDown()
def SetCompleter(self, completer):
"""
Initializes the autocompletion. The 'completer' has to be a function
with one argument (the current value of the control, ie. the query)
and it has to return two lists: formated (html) and unformated
suggestions.
"""
self.completer = completer
if DISABLE_AUTOCOMPLETE:
self.popup = DisabledSuggestionsPopup()
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
return
self.popup = SuggestionsPopup(self.mainApp)
self.mainApp.Bind(wx.EVT_MOVE, self.OnMove)
self.Bind(wx.EVT_TEXT, self.OnTextUpdate)
self.Bind(wx.EVT_SIZE, self.OnSizeChange)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
self.popup._suggestions.Bind(wx.EVT_LEFT_UP, self.OnSuggestionClicked)
#self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
def OnFocus(self, event):
#print 'focus'
if not self.noFocusSel:
wx.CallAfter(self.SelectAll)
self.skip_event = False
self.noFocusSel = 0
#print 'on focus'
def AdjustPopupPosition(self):
if DISABLE_AUTOCOMPLETE:
return
self.popup.Position = self.ClientToScreen((-1, self.Size.height)).Get()
self.popup.SetSize((self.Size.width +25, self.height))
def OnMove(self, event):
try:
self.AdjustPopupPosition()
except:
#print 'autoctrl error'
pass
event.Skip()
def OnTextUpdate(self, event):
event.Skip()
if DISABLE_AUTOCOMPLETE:
return
if self.skip_event:
self.skip_event = False
elif not self.queued_popup:
wx.CallLater(self.frequency, self.AutoComplete)
self.queued_popup = True
def AutoComplete(self):
if DISABLE_AUTOCOMPLETE:
self.queued_popup = False
return
self.queued_popup = False
if self.Value:
formated, unformated = self.completer(self.Value.lower())
if len(formated) > 0:
self.popup.SetSuggestions(formated, unformated)
self.AdjustPopupPosition()
self.Unbind(wx.EVT_KILL_FOCUS)
if len(formated) < 16:
h = (len(formated)) * 24
self.popup.SetSize((self.Size.width +25, h))
self.popup.ShowWithoutActivating()
self.noFocusSel = 1
#self.SetFocus()
wx.CallAfter(self.Bind, wx.EVT_KILL_FOCUS, self.OnKillFocus)
#wx.CallAfter(self.mainApp.Raise)
else:
self.popup.Hide()
else:
self.popup.Hide()
def OnSizeChange(self, event):
if DISABLE_AUTOCOMPLETE:
event.Skip()
return
self.popup.Size = (self.Size[0], self.height)
event.Skip()
def OnKeyDown(self, event):
key = event.GetKeyCode()
#print 'in key down1', key
if DISABLE_AUTOCOMPLETE:
if key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
if self.mainApp.dctNB.dctPanel.ac.HasFocus():
self.mainApp.OnDctViewEntry()
return
elif self.mainApp.cmtNB.cmtPanel.ac.HasFocus():
self.mainApp.OnCmtViewEntry()
return
elif self.mainApp.noteEdit.ac.HasFocus():
self.mainApp.currentNoteRef = self.GetValue()
self.mainApp.OnNotesUpdate()
self.mainApp.noteSync = False
return
event.Skip()
return
if key == wx.WXK_UP:
self.popup.CursorUp()
return
elif key == wx.WXK_DOWN:
if not self.popup.Shown:
self.OnButtonClick()
self.popup.CursorDown()
return
elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER) and self.popup.Shown:
self.skip_event = True
item = self.popup.GetSelectedSuggestion()
#print item, 'item'
self.ChangeValue(item)
self.SetInsertionPointEnd()
#wx.CallAfter(self.SetFocus)
self.popup.Hide()
self.noFocusSel = 0
if self.mainApp.dctNB.dctPanel.ac.HasFocus():
self.mainApp.OnDctViewEntry()
elif self.mainApp.cmtNB.cmtPanel.ac.HasFocus():
self.mainApp.OnCmtViewEntry()
elif self.mainApp.noteEdit.ac.HasFocus():
self.mainApp.currentNoteRef = item
self.mainApp.OnNotesUpdate()
self.mainApp.noteSync = False
return
elif key == wx.WXK_HOME:
self.popup.CursorHome()
elif key == wx.WXK_END:
self.popup.CursorEnd()
elif event.ControlDown() and chr(key).lower() == "a":
self.SelectAll()
elif key == wx.WXK_ESCAPE:
self.popup.Hide()
return
event.Skip()
def OnSuggestionClicked(self, event):
self.skip_event = True
n = self.popup._suggestions.HitTest(event.Position)
item = self.popup.GetSuggestion(n)
#print(item, 'item')
if not item:
print('No item')
return
self.ChangeValue(item)
#wx.CallAfter(self.SetFocus)
event.Skip()
self.popup.Hide()
self.noFocusSel = 0
if event.GetEventObject() == self.mainApp.dctNB.dctPanel.ac.popup._suggestions:
self.mainApp.OnDctViewEntry()
elif event.GetEventObject() == self.mainApp.cmtNB.cmtPanel.ac.popup._suggestions:
self.mainApp.OnCmtViewEntry(True)
elif event.GetEventObject() == self.mainApp.noteEdit.ac.popup._suggestions:
self.mainApp.OnSaveNotes()
syncState = self.mainApp.noteSync
self.mainApp.noteSync = True
#self.mainApp.currentNoteRef = item
self.mainApp.OnNotesUpdate()
self.mainApp.noteSync = syncState
def OnSuggestionKeyDown(self, event):
key = event.GetKeyCode()
#print 'in key down2', key
if key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
self.skip_event = True
item = self.popup.GetSelectedSuggestion()
self.ChangeValue(item)
#wx.CallAfter(self.SetFocus)
self.popup.Hide()
self.noFocusSel = 0
if self.mainApp.dctNB.dctPanel.ac.HasFocus():
self.mainApp.OnDctViewEntry()
elif self.mainApp.cmtNB.cmtPanel.ac.HasFocus():
self.mainApp.OnCmtViewEntry(True)
elif self.mainApp.noteEdit.ac.HasFocus():
self.mainApp.OnSaveNotes()
syncState = self.mainApp.noteSync
self.mainApp.noteSync = True
self.mainApp.currentNoteRef = item
self.mainApp.OnNotesUpdate()
self.mainApp.noteSync = syncState
event.Skip()
def OnKillFocus(self, event):
print ('in kill')
if not self.mainApp.FindFocus() or self.mainApp.FindFocus() == self:
return
if not self.popup.IsActive():
self.noFocusSel = 0
wx.CallAfter(self.popup.Hide)
event.Skip()