I need to implement pagination for list of entities in a telegram bot's reply keyboard.
the problem is that i need to have a show more button that loads next set of entities and i don't know how to get the page that user is currently viewing.
the next problem is how to know which list of entities is user currently viewing.
the only way that there is in my mind is to store the current state of user in database or cache it but i don't know that is there some way to add additional data to telegram reply keyboard's button or some other way to do this so i don't have to do this load of work.
You can add page number to message text or callback_data, and editMessage method is helpful.
For instance, this case using callback_data to pass current page and actions.
I started with this model once. Maybe it's helpful for someone else:
class PaginationModel(Generic[T]):
def __init__(self, all_items: Iterable[T] = None, items_per_page: int = 1):
self.all_items = list(all_items or [])
self._current_page = 0
self.items_per_page = items_per_page
#property
def _begin(self):
return self._current_page * self.items_per_page
#property
def _end(self):
return min((self._current_page + 1) * self.items_per_page, self.item_count)
#property
def item_count(self):
return len(self.all_items)
#property
def page_items(self) -> List[T]:
return self.all_items[self._begin : self._end]
#property
def current_page_number(self) -> int:
return self._current_page + 1
#property
def has_next_page(self) -> bool:
return self._end < self.item_count
#property
def has_previous_page(self) -> bool:
return self._begin > 0
def flip_next_page(self):
self._current_page += 1
def flip_previous_page(self):
self._current_page -=
Related
I have implemented a delegate and model on a TreeView, which contains an spinbox on the column number one, but the number format is not the correct one. But, after I click the spinbox, it is setted correctly. I am using PyQt5.
Here I attached two images which explain it much better.
Value setted on a first time
The value is seen correctly after click the spinbox
Here is the code where spinbox are initialized on a first time:
def createEditor(self, parent, option, index):
item = index.internalPointer()
if index.column() == 0:
editor = super().createEditor(parent, option, index)
elif index.column() == 1:
name = item.get_data()[0]
data = item.get_data()[1]
editor = QtWidgets.QSpinBox(parent, minimum=-9999999999, maximum=9999999999)
# editor.setEditTriggers(QAbstractItemView.SelectedClicked)
editor.setObjectName(name)
editor.setValue(data)
editor.setStyleSheet("""
border-radius: 0px;
""")
editor.installEventFilter(self)
else:
editor = super().createEditor(parent, option, index)
return editor
I think that I missed a comitData somewhere, but I don't know where is the better place to emit it, or if it is the correct way to do that.
Thanks you,
Marcel
The problem was resolved.
It was not in the delegate, and it has not got any relationship about QSpinBox, but the model and str format.
On QtCore.Qt.DisplayRole role on function data, I did the correct conversion, as I show on the following code:
def data(self, in_index, role):
if not in_index.isValid():
return QVariant()
node = in_index.internalPointer()
if role == QtCore.Qt.DisplayRole:
if node.data(in_index.column()) is None:
return ''
if in_index.column() == 1:
return str(float(node.data(in_index.column())))
return str(node.data(in_index.column()))
Thanks you all.
Regards,
Marcel
I'm trying to generate dynamic forms using Flask-WTF to create a new product based on some templates. A product will have a list of required key-value pairs based on its type, as well as a list of parts required to build it. The current relevant code looks as follows:
forms.py:
class PartSelectionForm(Form):
selected_part = QuerySelectField('Part', get_label='serial', allow_blank=True)
part_type = StringField('Type')
slot = IntegerField('Slot')
required = BooleanField('Required')
def __init__(self, csrf_enabled=False, *args, **kwargs):
super(PartSelectionForm, self).__init__(csrf_enabled=False, *args, **kwargs)
class NewProductForm(Form):
serial = StringField('Serial', default='', validators=[DataRequired()])
notes = TextAreaField('Notes', default='')
parts = FieldList(FormField(PartSelectionForm))
views.py:
#app.route('/products/new/<prodmodel>', methods=['GET', 'POST'])
#login_required
def new_product(prodmodel):
try:
model = db.session.query(ProdModel).filter(ProdModel.id==prodmodel).one()
except NoResultFound, e:
flash('No products of model type -' + prodmodel + '- found.', 'error')
return redirect(url_for('index'))
keys = db.session.query(ProdTypeTemplate.prod_info_key).filter(ProdTypeTemplate.prod_type_id==model.prod_type_id)\
.order_by(ProdTypeTemplate.prod_info_key).all()
parts_needed = db.session.query(ProdModelTemplate).filter(ProdModelTemplate.prod_model_id==prodmodel)\
.order_by(ProdModelTemplate.part_type_id, ProdModelTemplate.slot).all()
class F(forms.NewProductForm):
pass
for key in keys:
if key.prod_info_key in ['shipped_os','factory_os']:
setattr(F, key.prod_info_key, forms.QuerySelectField(key.prod_info_key, get_label='version'))
else:
setattr(F, key.prod_info_key, forms.StringField(key.prod_info_key, validators=[forms.DataRequired()]))
form = F(request.form)
if request.method == 'GET':
for part in parts_needed:
entry = form.parts.append_entry(forms.PartSelectionForm())
entry.part_type.data=part.part_type_id
entry.slot.data=slot=part.slot
entry.required.data=part.required
entry.selected_part.query = db.session.query(Part).join(PartModel).filter(PartModel.part_type_id==part.part_type_id, Part.status=='inventory')
if form.__contains__('shipped_os'):
form.shipped_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.__contains__('factory_os'):
form.factory_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.validate_on_submit():
...
Everything works as expected on a GET request, but on the validate_on_submit I get errors. The error is that all of the queries and query_factories for the selected_part QuerySelectFields in the list of PartSelectionForms is none, causing either direct errors in WTForms validation code or when Jinja2 attempts to re-render the QuerySelectFields. I'm not sure why this happens on the POST when everything appears to be correct for the GET.
I realized that although I set the required queries on a GET I'm not doing it for any PartSelectionForm selected_part entries on the POST. Since I already intended part_type, slot, and required to be hidden form fields, I added the following immediately before the validate_on_submit and everything works correctly:
for entry in form.parts:
entry.selected_part.query = db.session.query(Part).join(PartModel).\
filter(PartModel.part_type_id==entry.part_type.data, Part.status=='inventory')
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'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.
I'm start using Django-MPTT app to get a tree-based approach on my Django-site pages.
For ex. I have pages with sub pages:
Trance:
Vocal Trance(sub page)
Hard Trance(sub page)
Breaks:
Atmo Breaks(sub page)
Progressive Breaks(sub page)
How can I get access to them from urls.py?
What pattern will help?
Do I need to store Full_path in model or it can be done via url pattern?
I assume you mean you want to do URLs like this:
/trance/
/trance/vocal-trance/
/trance/hard-trace/
/breaks/
/breaks/atmo-breaks/
/breaks/progressive-breaks/
If so, it's probably best to store the url fragment in your model. Something like:
from mptt.models import MPTTModel
from django.db import models
from django.template.defaultfilters import slugify
class Page(MPTTModel):
name = models.CharField(max_length=50)
slug = models.CharField(max_length=50,null=True)
url = models.CharField(max_length=255,null=True)
def save(self, *args, **kwargs)
if self.slug is None:
# create a slug that's unique to siblings
slug = slugify(self.name)
self.slug = slug
siblings = self.get_siblings()
i = 1
while siblings.filter(slug=self.slug).exists():
i += 1
self.slug = slug + '-%d' % i
# now create a URL based on parent's url + slug
if self.parent:
self.url = '%s/%s' % (self.parent.url, self.slug)
else:
self.url = self.slug
super(Page, self).save(*args, **kwargs)
Then add a URL pattern:
(r'^pages/(?P<page_url>[\w\d_/-]+)/$', 'pages.views.show_page'),
And in your view you can just fetch the right page:
def show_page(request, page_url=None):
page = get_object_or_404(Page, url=page_url)
...
Thank you for your attention to my problem.
See,How I finally do it.
models.py
class WebPage(MPTTModel):
slug=RuSlugField(max_length=20,unique=True)
title=models.CharField(max_length=50)
content=models.TextField()
parent=TreeForeignKey('self',null=True,blank=True,related_name='children')
class MPTTMeta:
order_insertion_by=['slug']
def get_absolute_url(self):#TODO:: replace with get_ancestors
url = "/%s/" % self.slug
page = self
while page.parent:
url = "/%s%s" % (page.parent.slug,url)
page = page.parent
return url
urls.py
urlpatterns = patterns('website.views',
url(r"^add/$", "add_page",name="add"),
url(r"^(?P<full_slug>.*)/add/$", "add_page",name="add"),
url(r"^(?P<full_slug>.*)/edit/$", "edit_page",name="edit"),
url(r'^$', ListView.as_view(model=WebPage,template_name='index.html',context_object_name="webpages_list",),name='index'),
url(r"^(?P<full_slug>.*)/$", "page", name="page"),
)
views.py
def page(request, full_slug):
# Make a list from full_slug.
# For ex. /trance/progressive_trance/fonarev -> ['trance','progressive_trance','fonarev']
slugs=full_slug.split('/')
page=None
# Get a page by it's slug
if len(slugs)>1:
page=get_object_or_404(WebPage,slug=slugs[-1])#slugs=['trance','vocal_trance'] -> 'vocal_trance'
elif len(slugs)==1:
page=get_object_or_404(WebPage,slug=slugs[0])#slugs=['trance'] -> 'trance'
# Check if page url matches requested full_slug
if page.get_absolute_url().strip('/') == full_slug:
return render_to_response('page.html', {'page': page},context_instance=RequestContext(request))
else:
raise Http404
def edit_page(request,full_slug):
slugs=full_slug.split('/')
page=None
if len(slugs)>1:
page=get_object_or_404(WebPage,slug=slugs[-1])
elif len(slugs)==1:
page=get_object_or_404(WebPage,slug=slugs[0])
if not page.get_absolute_url().strip('/') == full_slug:
raise Http404
# Send POST data for update an existing page.Update a page.
if request.method=='POST':
form=WebPageForm(request.POST, instance=page)
if form.is_valid():
form.save()
return HttpResponseRedirect(page.get_absolute_url())
# Render a form to edit data for existing page
else:
form=WebPageForm(instance=page)
return render_to_response('edit_page.html',{'form':form,},context_instance=RequestContext(request))
def add_page(request,full_slug=None):
parent_page=None
slug=None
if full_slug:
slug=full_slug.split('/')
# If there is a slug in REQUEST(ex.'trance')->we need to add a new_page to her parent_page.
# So get a parent page.
if slug:
if len(slug)>1:
parent_page=get_object_or_404(WebPage,slug=slug[-1])
elif len(slug)==1:
parent_page=get_object_or_404(WebPage,slug=slug[0])
# Create a new_page
if request.method=='POST':
form=WebPageForm(request.POST)
if form.is_valid():
new_page=form.save(commit=False)
if parent_page:
new_page.parent=parent_page
new_page.save()
return HttpResponseRedirect(new_page.get_absolute_url())
# Return an unbounded form
else:
form=WebPageForm()
return render_to_response('add_page.html',{'form':form,},context_instance=RequestContext(request))
The trick is in we have to check if the page really exists accessing to it via full_slug:
if not page.get_absolute_url().strip('/') == full_slug:
raise Http404
Otherwise, it could be wrong allowing to check only by slug.
There's also a django app that will do the work for you: django-mptt-urls
def get_absolute_url(self):
return '/'.join([x['slug'] for x in self.get_ancestors(include_self=True).values()])