How to use css's :after for widgets in pyqt - python

I am thinking to create a PyQt6 Application.
I want that application to be more beautiful and modern. So I found some good looking css buttons and chose this codepen button:
* {
margin: 0;
padding: 0;
}
html,
body {
box-sizing: border-box;
height: 100%;
width: 100%;
}
body {
background: #FFF;
font-family: 'Noto Sans JP', sans-serif;
font-weight: 400;
}
.buttons {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
text-align: center;
width: 100%;
height: 100%;
margin: 0 auto;
/* padding: 2em 0em; */
}
.container {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
background-color: #FFF;
padding: 40px 0px;
width: 240px;
}
h1 {
text-align: left;
color: #444;
letter-spacing: 0.05em;
margin: 0 0 0.4em;
font-size: 1em;
}
p {
text-align: left;
color: #444;
letter-spacing: 0.05em;
font-size: 0.8em;
margin: 0 0 2em;
}
.btn {
letter-spacing: 0.1em;
cursor: pointer;
font-size: 14px;
font-weight: 400;
line-height: 45px;
max-width: 160px;
position: relative;
text-decoration: none;
text-transform: uppercase;
width: 100%;
}
.btn:hover {
text-decoration: none;
}
/*btn_background*/
.effect01 {
color: #FFF;
border: 4px solid #000;
box-shadow:0px 0px 0px 1px #000 inset;
background-color: #000;
overflow: hidden;
position: relative;
transition: all 0.3s ease-in-out;
}
.effect01:hover {
border: 4px solid #666;
background-color: #FFF;
box-shadow:0px 0px 0px 4px #EEE inset;
}
/*btn_text*/
.effect01 span {
transition: all 0.2s ease-out;
z-index: 2;
}
.effect01:hover span{
letter-spacing: 0.13em;
color: #333;
}
/*highlight*/
.effect01:after {
background: #FFF;
border: 0px solid #000;
content: "";
height: 155px;
left: -75px;
opacity: .8;
position: absolute;
top: -50px;
-webkit-transform: rotate(35deg);
transform: rotate(35deg);
width: 50px;
transition: all 1s cubic-bezier(0.075, 0.82, 0.165, 1);/*easeOutCirc*/
z-index: 1;
}
.effect01:hover:after {
background: #FFF;
border: 20px solid #000;
opacity: 0;
left: 120%;
-webkit-transform: rotate(40deg);
transform: rotate(40deg);
}
<div class="buttons">
<div class="container">
<h1>光の反射</h1>
<p>Light reflection</p>
<span>Hover</span>
</div>
</div>
My question is: how can I use pyqt's functions to perform the :after.
I guess I cant just use a widget's stylesheet to perform.
Here is the Python Code that I have written so far:
from PyQt6.QtCore import QRect, Qt
from PyQt6.QtGui import QEnterEvent
from PyQt6.QtWidgets import QGraphicsOpacityEffect, QMainWindow, QPushButton
class QButton(QPushButton):
def __init__(self , main_window: QMainWindow):
super().__init__(main_window)
self.main_window = main_window
self.setText("Welcome")
self.render()
def render(self):
self.setStyleSheet("""
QPushButton{
background-color: #2E3440;
letter-spacing: 0.1px;
font-size: 14px;
font-weight: 400;
line-height: 45px;
text-decoration: none;
color: #FFF;
border: 4px solid #4C566A;
}
""")
self.setFlat(True)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.setGeometry(QRect(200 , 200 , 200 , 50))
def setText(self , text: str):
text = text.upper()
super().setText(text)
def enterEvent(self, event: QEnterEvent) -> None:
self.setStyleSheet("""
background-color: #D8DEE9;
letter-spacing: 0.13em;
color: #2E3440;
border: 0px;
""")
opacity = QGraphicsOpacityEffect()
opacity.setOpacity(.8)
self.setGraphicsEffect(opacity)
return super().enterEvent(event)
class Renderer:
def __init__(self, main_window: QMainWindow) -> None:
self.main_window = main_window
def render(self):
button = QButton(self.main_window)
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
renderer = Renderer(window)
renderer.render()
window.show()
exit(app.exec())

Unfortunately, you can't. Qt Style Sheets are fundamentally based on CSS 2.1, and only support its basic implementation (see the syntax documentation).
If you want a more customized and advanced behavior, you have only two options:
subclass the widget and/or use QProxyStyle;
use QML;

Related

Adding scroll to the final output view using ipywidgets

I am trying to create an app based on ipywidgets. But unfortunately the scroll is not appearing as I expect it to be. Instead the scrolls are appearing on every output element, whereas I want a single scroll if the height of the content in any of the output element exceeds. Moreover it should not cut the visibility of the content in any of the output.
%%html
<style>
.out1{
width: 700px;
height: 400px;
display: flex;
justify-content: center;
padding: 10px 10px 10px 10px;
border: 1px solid black;
}
.out2{
width: 350px;
height: 400px;
display: flex;
justify-content: center;
padding: 20px 5px 10px 10px;
border: 1px solid black;
}
.out3{
width: 350px;
height: 400px;
display: flex;
justify-content: center;
padding: 20px 5px 10px 10px;
border: 1px solid black;
}
.out4{
width: 700px;
height: 400px;
display: flex;
justify-content: center;
padding: 20px 10px 0px 10px;
border: 1px solid black;
}
.out_box{
border: 1px solid black;
height: 800px;
width: 700px;
overflow: auto;
}
</style>
out1=widgets.Output()
out1.add_class('out1')
out2=widgets.Output()
out2.add_class('out2')
out3=widgets.Output()
out3.add_class('out3')
out4=widgets.Output()
out4.add_class('out4')
box1=widgets.Box([out1], scroll=False)
box2=widgets.HBox([out2,out3], scroll=False)
box3=widgets.Box([out4], scroll=False)
out_box=widgets.VBox([box1,box2,box3])
out_box.add_class('out_box')
Attached is the snap, which I am getting. Results with multiple scroll bars. But I want a single scroll bar at the right end.

custom text position on ipywidgets button

I am trying to put a "+" sign on a button that reaches all the edges. This is a minimal example, from a Jupyter notebook, first a style:
%%html
<style>
.button_style{
font-size:155px;
color: black;
}
</style>
and the button itself:
import ipywidgets as ipyw
button = ipyw.Button(description='+', style={'button_color':'blue'},
layout=ipyw.Layout(width='80px', height='80px'))
button.add_class("button_style")
As you can see, what I tried was making the font size big enough to reach the button edges.
But the problem is that the text is not aligned with the middle of the button:
I've played with options like text-align, left-padding, left-margin etc, but they all affect the entire button, i.e. they translate or deform the entire blue square, rather than just the text within it.
Is there any way to do this? Ideally I'd be able to change the center of the cross but always have it reach all edges, and without making the cross itself super fat.
But less than ideal solutions are also welcome.
Update:
So the result I am looking for is this:
where the center and ideally also the thickness of the lines can be configured. It doesn't matter if the result is achieved with a '+', as long as the button looks like this and the background color can still be changed with style={'button_color': ..}.
As #johannchopin mentioned, this can be done with pseudo-elements. I suggest a solution similar to his but without the inner .custom-plus div. It's better because it will allow you to use the ipywidgets as-is and just add the styling.
This will allow you to use the button from ipywidgets like this:
Style
%%html
<style>
.button_style {
--size: 80px;
--line-color: black;
--line-color-horizontal: var(--line-color);
--line-color-vertical: var(--line-color);
--line-stroke: calc(0.125 * var(--size));
--line-stroke-horizontal: var(--line-stroke);
--line-stroke-vertical: var(--line-stroke);
--line-distance: calc(0.2 * var(--size));
--line-distance-right: var(--line-distance);
--line-distance-bottom: var(--line-distance);
--line-hover: lightblue;
--background: blue;
--background-hover: var(--background);
color: black;
padding: 0;
background: var(--background);
width: var(--size);
height: var(--size);
position: relative;
border: none;
cursor: pointer;
}
.button_style:hover {
background: var(--background-hover);
}
.button_style:hover::before, .button_style:hover::after {
background: var(--line-hover);
}
.button_style::before, .button_style::after {
position: absolute;
content: "";
transition: background 250ms;
}
.button_style::before {
left: 0;
bottom: var(--line-distance-bottom);
width: 100%;
height: var(--line-stroke-horizontal);
background: var(--line-color-horizontal);
}
.button_style::after {
right: var(--line-distance-right);
top: 0;
height: 100%;
width: var(--line-stroke-vertical);
background: var(--line-color-vertical);
}
</style>
Definition
import ipywidgets as ipyw
button = ipyw.Button(description='+', style={'--size':'80px', '--background': 'blue' })
button.add_class("button_style")
You could probably also remove the layout option to define the size of the button since it's optional and we already set the size using CSS variables (--size). Also, using the CSS variables we can set the background color or even change colors on hover (see my snippet for an example).
.button_style {
--size: 80px;
--line-color: black;
--line-color-horizontal: var(--line-color);
--line-color-vertical: var(--line-color);
--line-stroke: calc(0.125 * var(--size));
--line-stroke-horizontal: var(--line-stroke);
--line-stroke-vertical: var(--line-stroke);
--line-distance: calc(0.2 * var(--size));
--line-distance-right: var(--line-distance);
--line-distance-bottom: var(--line-distance);
--line-hover: lightblue;
--background: blue;
--background-hover: var(--background);
color: black;
padding: 0;
background: var(--background);
width: var(--size);
height: var(--size);
position: relative;
border: none;
cursor: pointer;
}
.button_style:hover {
background: var(--background-hover);
}
.button_style:hover::before,
.button_style:hover::after {
background: var(--line-hover);
}
.button_style::before,
.button_style::after {
position: absolute;
content: "";
transition: background 250ms;
}
.button_style::before {
left: 0;
bottom: var(--line-distance-bottom);
width: 100%;
height: var(--line-stroke-horizontal);
background: var(--line-color-horizontal);
}
.button_style::after {
right: var(--line-distance-right);
top: 0;
height: 100%;
width: var(--line-stroke-vertical);
background: var(--line-color-vertical);
}
<button class="button_style"></button>
<button class="button_style" style="--line-distance: 35px; --background: purple; --line-hover: white"></button>
<button class="button_style" style="--line-distance-bottom: 10px; --line-distance-right: 65px; --background: hsl(348, 100%, 61%); --line-color: hsl(48, 100%, 67%); --line-hover: white"></button>
<button class="button_style" style="--line-distance-bottom: 20px; --line-distance-right: 20px; --background: orange; --line-stroke-horizontal: 25px; --line-hover: orange; --background-hover: black"></button>
<div style="margin: 1em 0">
<button class="button_style" style="--size: 40px"></button>
</div>
I would recommend you to create these 2 bars by using ::before and ::after elements in pure css:
.button_style::before {
position: absolute;
left: 0;
bottom: 15px;
content: "";
width: 100%;
height: 10px;
background: black;
}
.button_style::after {
position: absolute;
top: 0;
right: 15px;
content: "";
height: 100%;
width: 10px;
background: black;
}
The final result look like this:
.button_style {
position: relative;
font-size: 155px;
color: black;
line-height: 0;
padding: 0;
background: blue;
width: 80px;
height: 80px;
}
.button_style::before {
position: absolute;
left: 0;
bottom: 15px;
content: "";
width: 100%;
height: 10px;
background: black;
}
.button_style::after {
position: absolute;
top: 0;
right: 15px;
content: "";
height: 100%;
width: 10px;
background: black;
}
<button class="button_style"></button>
Have a look at the Jsfiddle.

Creating matrix in python with pandas and numpy

I am facing the problem to creating a matrix in python which divides but instead it multiplies.
I have two dataframes:
df_in = pd.DataFrame([[77.279999], [80.099998]], index=[2019, 2020], columns=['Price'])
df_out = pd.DataFrame([[71.849998], [77.400002]], index=[2019, 2020], columns=['Price])
Now I will create the matrix:
df_matrix = pd.DataFrame(np.outer(df_in, df_out), df_in.index, df_out.index)
The output I get is:
<style type="text/css">
table.tableizer-table {
font-size: 12px;
border: 1px solid #CCC;
font-family: Arial, Helvetica, sans-serif;
}
.tableizer-table td {
padding: 4px;
margin: 3px;
border: 1px solid #CCC;
}
.tableizer-table th {
background-color: #104E8B;
color: #FFF;
font-weight: bold;
}
</style>
<table class="tableizer-table">
<thead><tr class="tableizer-firstrow"><th></th><th>2019</th><th>2020</th></tr></thead><tbody>
<tr><td>2019</td><td>5552.567794</td><td>5755.184768</td></tr>
<tr><td>2020</td><td>5981.472023</td><td>6199.740004</td></tr>
</tbody></table>
It is multiplying instead of dividing. The next problem I am facing is that if
df_in.index > df_out.index
then value should be 0.
The result that I would like to see is:
<style type="text/css">
table.tableizer-table {
font-size: 12px;
border: 1px solid #CCC;
font-family: Arial, Helvetica, sans-serif;
}
.tableizer-table td {
padding: 4px;
margin: 3px;
border: 1px solid #CCC;
}
.tableizer-table th {
background-color: #104E8B;
color: #FFF;
font-weight: bold;
}
</style>
<table class="tableizer-table">
<thead><tr class="tableizer-firstrow"><th></th><th>2019</th><th>2020</th></tr></thead><tbody>
<tr><td>2019</td><td>1,075574</td><td>1,114822</td></tr>
<tr><td>2020</td><td>0</td><td>1,034883</td></tr>
</tbody></table>
So thanks to all for your advices.
You can do divide.outer:
pd.DataFrame(np.divide.outer(df_in,df_out)[:,0,:,0], df_in.index, df_out.index)
Output:
2019 2020
2019 1.075574 0.998450
2020 1.114823 1.034884

I need a div to be pushed down when several options from a dcc.dropdown menu have been selected (dash component, python)

I am having trouble making the div below the 'multi' dropdown (dcc.dropdown from dash) to be pushed down when several options from the menu have been selected instead of overlapping as in the image below (or be sent to the back depending on the z-index). The dropdown is inside another div. I´ve tried changing css display and position with no positive outcome yet.
The code looks something like this:
html.Div([
html.Div(children=html.H2('SIMILAR PLAYERS', className='titulo_ventanah2'), className='titulo_ventana'),
html.Div(children=[(html.I(className='search')),'Search by:'],style={'display':'inline-block','padding-left':'15%', 'font-size':'13px'}),
html.Div(children=(dcc.Dropdown(style={'height':'20px', 'font-size':'14px'},persistence_type='session')),style={'display':'inline-block', 'padding':'0px 0px 0px 10px', 'width':'200px', 'margin-top':'5px'}),
html.Div(children=(dcc.Dropdown(multi=True,style={'height':'20px', 'font-size':'14px'})),style={'padding':'0px 0px 0px 10px', 'width':'400px', 'margin-top':'5px'}, className='similardiv'),
html.Div([
html.Div(children=[html.Div(html.H3('Top 15 most similar players',className='titulo_ventanah3'),className='titulo_ventanaint'),
html.Hr(),
html.Div([children=dcc.Graph())])],className='similar_players'),
])
],className='container1')
There is also CSS code for some components:
.container1 {
position: fixed;
width:80%;
height:100%;
display:inline-block;
left:20%;
margin: 0 auto;
padding: 0 0px;
box-sizing: border-box;
overflow: hidden;
top:0;
overflow:auto;
background-color: #f5f5f5;
}
.titulo_ventana {
top:0;
display:inline-block;
background-color: #f5f5f5;
padding-left:2%;
margin: 0px 0px 0px 0px;
}
.titulo_ventanah2 {
text-decoration: none;
font-size: 20px;
line-height:45px;
color: #8f8f8f;
margin: 0px 0px 0px 0px;
display: inline-block;
letter-spacing: 0.01em;
}
.titulo_ventanaint {
overflow: hidden;
z-index:5;
height:39px;
display:inline-block;
background-color: white;
}
.titulo_ventanah3 {
max-width:100%;
text-decoration: none;
font-size: 14px;
color: black;
line-height:45px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 10px;
text-align: left;
overflow:hidden;
letter-spacing: 0.01em;
}
.similar_players {
width: 97%;
overflow: auto;
height:540px;
font-size: 15px;
color: #002e5c;
background-color: white;
margin-top:1.5%;
margin-left:1.5%;
display: block;
position:relative;
z-index:1;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}
.similardiv {
position:relative;
z-index:2;
display:inline-block;
}
I am hoping the solution is with a change in the display or position property of an element but I believe it has something to do with the default css for the dash component which can be found here.
You can check out the dashboard in link
If I change the position of the .Select-menu-outer to relative as suggested by CBroe in the comments, the following will happen only when the menu is opened:
The problem was to set the height of the dcc.Dropdown to 20px, by just deleting 'height':'20px' the problem got solved

How to remove white background (on top and bottom) from popup of QComboBox?

I am creating a GUI app with qt5 and pyqt5.
I am trying to create a dark theme but I've got a problem with QComboBox.
When I tried to make a dark background on the QListView I get a white border or whatever its name on top and the bottom of the list.
I tried a lot of ways, like padding or marge changing of values but nothing helped
I tried something mentioned already here
Remove QListView background
But always the same.
QComboBox {
font: 12pt Fira Sans Condensed;
background-color: #2e2e2e;
border-top: 0px solid #3e3e3e;
border-left: 0px solid #3e3e3e;
border-right: 0px solid #3e3e3e;
border-bottom: 2px solid #3e3e3e;
padding: 5%;
max-height: 30px;
min-width: 140px;
color: white;
selection-background-color: #5e5e5e;
}
QComboBox::drop-down {
border: none;
}
QComboBox::down-arrow {
image: url(icons/QComboBox/down-arrow.png);
width: 25px;
height: 25px;
border-width: 0px;
padding-right: 10px;
}
QComboBox::down-arrow:pressed {
position: relative;
top: 1px; left: 1px;
}
QListView {
font: 12pt Fira Sans Condensed;
background-color: #2e2e2e;
outline: 0;
color: white;
selection-background-color: #5e5e5e;
}
QListView::item {
min-height: 20px;
padding: 5%;
}
self.list = QtWidgets.QListView(self.window.comboBox)
self.window.comboBox.addItem("test1")
self.window.comboBox.addItem("test2")
self.window.comboBox.setView(self.list)
Here is what I get
Add I expect something without the white color on the top and bottom of the list
You have to set the color in the QFrame that is the parent of the view() directly:
self.window.comboBox.view().parentWidget().setStyleSheet('background-color: #2e2e2e;')

Categories