Custom RadioSelect Rendering - django

I'm trying to make Django's RadioSelect widget render horizontally. I found the following SO post, that I thought had solved the problem: Align radio buttons horizontally in django forms, it basically states to use a custom Renderer, as follows:
class HorizontalRadioRenderer(forms.RadioSelect.renderer):
def render(self):
return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
class MyForm(ModelForm):
select=forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect(renderer=HorizontalRadioRenderer
But when I implement this, I still get the radio selection buttons rendering vertically. Here's a screenshot.
This is a seriois problem for my form. Any idea why it isn't working correctly? If it matters, the form is being rendered in a table.
Thanks
UPDATE:
Ok, I've tried something else. The renderer is now:
def render(self):
internal=''.join(['<span id="radio">%s</span>' % w for w in self])
return mark_safe(u'%s' %internal)
and I've added the CSS to my stylesheet:
#radio{
width: 100px;
float: left;
}
This renders the RadioBoxes inline, and looks great. But now there's an even bigger problem. As seen in my screenshot above, I have 2 choices, Yes and N/A. Right now, if I click on either yes or N/A, N/A gets selected. I thought this might be because they were both in spans with the same id, but if I change it to class="radio" the same thing happens. If I remove float: left from the CSS, then it works normally (but, of course, isn't displayed horizontally). Any idea what's causing this?

Whoa: okay.
Still not sure why having them with the same id or class caused them to behave as the same radio box, but I've come up with a solution.
The renderer now reads as follows:
def render(self):
internal = ''.join(['<li>%s</li>' % w for w in self])
return mark_safe(u'<div id="radio"><ul>%s</ul></div>' %internal)
This makes the radio boxes in an un-ordered list, surrounded by a div with an id of radio.
I then have the css:
#radio ul li label{
display:inline;
}
This puts them in a line. Nice and easy. Don't know why the other approach didn't work, when it sounded like it did for another SO user.

Related

Control text positioning inside a select box (Select2, django-autocomplete-light)

I have a modelform in which i've added a taggitselect2 widget as part of django-autocomplete-light.
This looks up tags from taggablemanager to allow an autocompletion. The autocomplete is working fine - but the alignment of the text inside the select box is off. The text aligns with the bottom of the select box, leaving a big gap between the top of the tag and the top of the select box. Easier with a picture:
https://imgur.com/a/WxFMLfF
forms.py
widgets = {
'tags': autocomplete.TaggitSelect2(
url='recordings:recording-autocomplete',
attrs={
'data-placeholder': 'Start typing to autocomplete...',
}
....inside def __init__
self.helper.layout = Layout(
Row(Column(Field('tags')),css_class='form-row'),
I've tried looking at styling options - this is a bootstrap project so ideally i would like the same styling you get with data_role="tagsinput" but if i assign that to the widget i guess it overrides the custom part and i get some broken output.
Couldn't work out why the default rendering is so poorly formatted so as a workaround I've just overridden the css.
On inspection, the selection elements have a 5px margin, enough to align them with the bottom. Hence applying the following in css:
.select2-selection__choice{
margin-top: 0px !important;
}
'fixes' the problem. Without '!important' it gets overridden to 5px, so this is required.

django form textarea allow vertical resize only

in my django project I made a form using
message = forms.CharField(widget=forms.Textarea)
It works out ok and I get this. The text area is resizeable both horizontally and vertically:
I would like to make it un-resizeable horizontally, what should I do? I tried adding "attrs=" to the textarea but can't figure out what to call... Please help... Thanks.
I believe you can achieve this with a css rule:
resize: vertical;
So, one way is to give it a class
message = forms.CharField(widget=forms.TextArea(attrs={"class": "message"}))
Then define a css rule
.message{
resize: vertical;
}

Show preview of image in form

I want to achieve the following in a django form, for a models.ImageField:
do not show the standard <input type="file"> button ("choose file" button)
show a preview of the current image, for already populated models
for empty (new) models, show a custom button
clicking on the image preview or the custom button will open a file dialog to select the image from the user's file system
selecting a new image replaces the custom button or the old image preview with the preview of the new image
This seems to me like a normal workflow to select images in a form, but I do not seem to find any fully working solution. All I can find involves hacking around several parts:
styling the label and hiding the standard "choose file" button: https://www.youtube.com/watch?v=4p2gTDZKS9Y
use a widget instead of the standard for forms.FileField.
I have tried to use:
class ImagePreviewWidget(Widget):
def render(self, name, value, attrs=None):
return mark_safe('<img src="/media/%s" width="100px"/>' % escape(value))
For the widget, and I am using this in the form like this:
class DesignCampaignForm(ModelForm):
brand_logo = FileField(widget=ImagePreviewWidget)
This is properly showing the preview of the existing image, but I am unable to click on it to select another file, and even if I was that would not update the preview.
Is there an already available solution for this simple use case?
I haven't been able to find a complete solution, so I have done the following:
use a widget to render a modified ClearableFileInput, rendering an image and an <input> element
style the <input> in the element with CSS, to hide it
make sure that clicking in the image triggers the hidden <input> element, wrapping the <img> in a <label> and using the for attribute
add some javascript to replace the image preview whenever the selection in the <input> element changes
whenever the selection is cleared, show the original preview
A gist can be found here.
In my final solution (not yet in the gist), I have added a button for when the image is not yet selected.
edit: Gist only works for Django before version 1.11.x.
class ClearableFileInput has since been gutted and changed

QT - multi-select

I would like to create a search-type text field in QT that can contain both standard text as well as what I would call "tags"... basically additional search terms that are individually highlighted and separated. I envision this looking like the multi-select in "Chosen" (Javascript library). http://harvesthq.github.com/chosen/
I have been unable to find anything similar through searching. It also seems that the standard QT text box types are not designed to have "sub-widgets".
It appears that QTextEdit supports HTML... that might be a possiblity... but the docs are not very clear to me as what is supported in terms of CSS (which I think would be required to get the desired formatting). http://doc.qt.io/qt-5/qtextedit.html#html-prop
Its funny... I got to the bottom of this submission page and realized I have to tag this (this is my first SO question)... This tag-adder box is almost exactly what I want!
There is no ready-to-use soultion that I know.
If I were to try implementing it, I would definitely use widget with layout, in which there are two types of child widgets: LineEdits (borderless to look like actual part of bigger widget) and buttons - code managing line edit changes would simply add new buttons before or after and if necesary splited linedit into tw with button between. This way is not interfering with qt programers intentions on how to use widgets and how to make them all fit together in one style.
If you want you can use custom widgets instead of buttons to provide remove icon.
As I wrote in my comment - if i have a bit more time I will try to make something like that myself.
Here is a very simple implementation of putting buttons in a QLineEdit as a user types, written in Python:
from PySide.QtCore import *
from PySide.QtGui import *
class Entry(QLineEdit):
def __init__(self):
QLineEdit.__init__(self)
self.buttons = []
self.backupText = ''
self.textEdited.connect(self.on_change)
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.layout.addStretch()
marginz = QLabel(' ')
marginz.show()
margin = marginz.width()
marginz.hide()
self.layout.setContentsMargins(margin, margin, margin, margin)
def on_change(self):
if self.text()[-1] == ' ' and not self.text().endswith(' '):
if len(self.text()) > len(self.backupText):
self.setText(self.text() + ' ')
self.buttons.append(QPushButton(self.text().split()[-1]))
self.layout.insertWidget(self.layout.count()-1, self.buttons[-1])
else:
self.setText(self.text()[0:-1])
self.buttons[-1].hide()
del self.buttons[-1]
self.backupText = self.text()
app = QApplication([])
window = QMainWindow()
window.setStyleSheet(
'QPushButton {border: 1px solid gray; background: lightgray; color: black;}')
entry = Entry()
window.setCentralWidget(entry)
window.show()
app.exec_()
It creates a QHBoxLayout and adds a button to it for each word you type, and takes the button away when you get rid of the word.
If you want to put a close button inside of each of the sub-widgets, you can make a custom widget for that too.
EDIT
As j_kubik's comment stated, systems with wide-margin buttons would cause the tag buttons to overlap the text a user is currently typing. I have modified the code to enforce the margins of the buttons inserted (with stylesheets), added an extra space for each space the user types, and set the QHBoxLayout's contentsMargins to be the same width as a space (""). Now the buttons will not overlap the text inserted.

Customizing RadioSelect

Hello I have a form with ChoiceField whose widget is set to RadioSelect
Now to override default html output one needs to subclass RadioFieldRenderer like this:
class SimpleRadioFieldRenderer(forms.widgets.RadioFieldRenderer):
def render(self):
"""Outputs widget without <ul> or <li> tags."""
return mark_safe(u'\n'.join([u'%s'
% force_unicode(w.tag()) for w in self]))
All is good now except I'd like to be able to render 1 radio button at a time from template.
Something like this:
{{ form.myfield.0 }}}
Or perhaps hanging it onto widget itself:
{{ form.myfield.field.widget.0 }}
What bugs me is that RadioFieldRenderer already implements __get_item__ to get just one RadioInput. The problem is that the renderer does not contain data, neither does the widget. And I'd really hate to mess with Field and BoundField.
I need this to inject html before/after each radiobutton and I'd like it to be done in the template. From the code it would be easy.
Any ideas are welcome.
I think this post in django-users may provide a way to do it (with an accessor function in the form class):
http://groups.google.com/group/django-users/msg/b60410692c7c60e2