mirror of
https://gitee.com/py2cn/pyminer.git
synced 2024-12-01 19:27:42 +08:00
修改为标准python包形式
This commit is contained in:
parent
de02877105
commit
b63ae81568
5
.flake8
Normal file
5
.flake8
Normal file
@ -0,0 +1,5 @@
|
||||
[flake8]
|
||||
ignore = W293,E301,E271,E265,W291,E722,E302,C901,E225,E128,E122,E226,E231
|
||||
max-line-length = 160
|
||||
exclude = tests/*
|
||||
max-complexity = 10
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -79,16 +79,13 @@ __pycache__/
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
venv*/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
.vscode/
|
||||
pyminer/config/qtlayout.ini
|
||||
pyminer2/config/qtlayout.ini
|
||||
pyminer2/config/customized/*
|
||||
pyminer2/extensions/packages/code_editor/customized/settings.json
|
||||
features/extensions/packages/code_editor/customized/settings.json
|
||||
*.pptx
|
||||
|
||||
test.py
|
||||
|
2
.mailmap
Normal file
2
.mailmap
Normal file
@ -0,0 +1,2 @@
|
||||
pyminer development team <team@py2cn.com>
|
||||
lixianglong <aboutlong@qq.com>
|
8
.pypirc
Normal file
8
.pypirc
Normal file
@ -0,0 +1,8 @@
|
||||
[distutils]
|
||||
index-servers =
|
||||
pypi
|
||||
|
||||
[pypi]
|
||||
repository = https://upload.pypi.org/legacy/
|
||||
username = pyminer
|
||||
password = pypi-AgEIcHlwaS5vcmcCJDBlYTM4NTgwLTVkZTktNDMyNy05MGU1LTEyZTgzZDkwNTZjYgACOHsicGVybWlzc2lvbnMiOiB7InByb2plY3RzIjogWyJweW1pbmVyIl19LCAidmVyc2lvbiI6IDF9AAAGIKggj-ecVRPT3zwKNytQivI-usyMhsPTHuPIrjZVO5aB
|
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@pyminer.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
821
LICENSE
821
LICENSE
@ -1,674 +1,165 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
42
MANIFEST.in
Normal file
42
MANIFEST.in
Normal file
@ -0,0 +1,42 @@
|
||||
include README.md
|
||||
include LICENSE
|
||||
include setup.py
|
||||
include MANIFEST.in
|
||||
include pytest.ini
|
||||
include .mailmap
|
||||
include .flake8
|
||||
|
||||
recursive-exclude tools *
|
||||
exclude tools
|
||||
exclude dist
|
||||
exclude CONTRIBUTING.md
|
||||
exclude .editorconfig
|
||||
|
||||
|
||||
# Load main dir but exclude things we don't want in the distro
|
||||
graft pyminer
|
||||
|
||||
# Documentation
|
||||
graft docs
|
||||
exclude docs/\#*
|
||||
exclude docs/man/*.1.gz
|
||||
|
||||
exclude .git-blame-ignore-revs
|
||||
|
||||
# Examples
|
||||
graft examples
|
||||
|
||||
# docs subdirs we want to skip
|
||||
prune docs/build
|
||||
prune docs/gh-pages
|
||||
prune docs/dist
|
||||
|
||||
# Patterns to exclude from any directory
|
||||
global-exclude *~
|
||||
global-exclude *.flc
|
||||
global-exclude *.yml
|
||||
global-exclude *.pyc
|
||||
global-exclude *.pyo
|
||||
global-exclude .dircopy.log
|
||||
global-exclude .git
|
||||
global-exclude .ipynb_checkpoints
|
159
README.md
159
README.md
@ -2,16 +2,16 @@
|
||||
<p></p>
|
||||
|
||||
<p align="center">
|
||||
<img src="pyminer2/ui/source/icons/logo.png" height="80"/>
|
||||
<img src="pyminer/resources/icons/logo.png" height="80"/>
|
||||
</p>
|
||||
|
||||
|
||||
<div align="center">
|
||||
|
||||
[![Stars](https://gitee.com/py2cn/pyminer/badge/star.svg?theme=gvp)](https://gitee.com/py2cn/pyminer/stargazers)
|
||||
[![Platform](https://img.shields.io/badge/python-v3.8-blue)](https://img.shields.io/badge/python-v3.7-blue)
|
||||
[![Platform](https://img.shields.io/badge/PyQt5-v5.15.0-blue)](https://img.shields.io/badge/PyQt5-v5.15.0-blue)
|
||||
[![License](https://img.shields.io/badge/license-GPL-blue)](https://img.shields.io/badge/license-GPL-blue)
|
||||
[![Platform](https://img.shields.io/badge/python-v3.8-blue)](https://img.shields.io/badge/python-v3.8-blue)
|
||||
[![Platform](https://img.shields.io/badge/PySide2-blue)](https://img.shields.io/badge/PySide2-blue)
|
||||
[![License](https://img.shields.io/badge/license-LGPL-blue)](https://img.shields.io/badge/license-LGPL-blue)
|
||||
|
||||
</div>
|
||||
|
||||
@ -19,33 +19,63 @@
|
||||
<a src="https://img.shields.io/badge/QQ%e7%be%a4-orange">
|
||||
<img src="https://img.shields.io/badge/QQ%e7%be%a4-945391275-orange">
|
||||
</a>
|
||||
<a src="https://img.shields.io/badge/QQ-454017698-orange">
|
||||
<img src="https://img.shields.io/badge/QQ-454017698-orange">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
`PyMiner` 是跨平台的,基于数据工作空间的数学工具。
|
||||
<h2>pyminer</h2>
|
||||
<h3> 开源、友好、跨平台的数据分析解决方案</h3>
|
||||
|
||||
</div>
|
||||
PyMiner一款基于数据工作空间的数学工具,通过加载插件的方式实现不同的需求,用易于操作的形式完成数学计算相关工作。
|
||||
pyminer 是一款基于Python的开源、跨平台数据分析环境。它以方便Python初学者为己任,在Python的知识理论和工作实践之间搭建桥梁,竭诚为初学者服务。
|
||||
|
||||
它开箱即用,大大减少配置解释器环境的繁琐性。不仅提供了编程运行的功能,还能够以交互式的形式进行常见的数据分析操作,减少代码编写和文档查阅的时间。
|
||||
|
||||
同时,pyminer将提供面向新手的快速入门教程,教程正由开发团队编写中。同时,我们诚挚希望与Python培训机构合作,让我们的产品帮助到更多学习Python的人。
|
||||
|
||||
pyminer通过加载各种插件实现不同的需求,开发者可以通过编写插件,将pyminer扩展的更强大、更趁手,甚至创建一番自己的商用程序。
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
## 🔈 声明
|
||||
pyminer 遵循LGPL协议,是一个开放、包容的开源项目,项目中的工具方法来源也非常广泛,因此允许并鼓励任何人在遵循LGPL协议的基础上:
|
||||
|
||||
1. 将pyminer用于商业、培训等任何合法场景;
|
||||
2. 复制、修改 pyminer中的任意代码且无需声明;
|
||||
3. 复制修改 pyminer官方文档;
|
||||
4. 鼓励自行写作 pyminer 相关的书籍、博客、文档等内容(收费也可);
|
||||
5. 鼓励播主、培训机构培训 pyminer工具的任何内容(收费也可);
|
||||
6. 对于商业应用的技术咨询,pyminer团队保留服务的收费权。
|
||||
pyminer希望成为一个伟大的开源项目,也希望得到大家的认可和赞美,仅此而已。
|
||||
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
## 🎉 技术说明
|
||||
|
||||
项目开发环境支持跨平台,windows,linux,mac 都支持!如果使用出现问题,欢迎提issue,建议使用Python3.8+PyQt5.15,基于python虚拟环境进行开发。
|
||||
1. 项目开发环境支持跨平台,windows,linux,mac 都支持。
|
||||
2. Python版本:支持Python3.5及以上,但建议使用Python3.8及以上版本,性能更好。
|
||||
3. Qt的Python接口:使用PySide2,版本为5.15.2。
|
||||
4. 项目开发环境使用PyCharm
|
||||
|
||||
注意:
|
||||
|
||||
- pyminer 的官方发行版本为Python3.8+PySide2-5.15.2。开发者可自行使用其他版本的Python解释器配置相关环境。
|
||||
- pyminer 曾经由PyQt5开发。但考虑到官方支持以及许可证的类型,我们已经迁移到了PySide2并改变许可证为LGPL。请勿使用PyQt5安装。
|
||||
- 当使用Python3.8配置环境时,不支持3.8.0等低版本的Python3.8解释器。当使用Python3.8时,请使用3.8.5或者更高版本的解释器。
|
||||
|
||||
- 如果使用出现问题,欢迎提issue。
|
||||
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
## 🎁 文档地址
|
||||
[https://gitee.com/py2cn/pyminer/wikis](https://gitee.com/py2cn/pyminer/wikis)
|
||||
- 项目文档:[https://gitee.com/py2cn/pyminer/wikis](https://gitee.com/py2cn/pyminer/wikis)
|
||||
- API文档:[http://py2cn.gitee.io/pyminer](http://py2cn.gitee.io/pyminer)
|
||||
- MATLAB与Numpy对比:[http://mathesaurus.sourceforge.net/matlab-numpy.html](http://mathesaurus.sourceforge.net/matlab-numpy.html)
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
@ -67,70 +97,91 @@ PyMiner一款基于数据工作空间的数学工具,通过加载插件的方
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
#### Windows安装
|
||||
### 发行版下载(仅Windows系统)
|
||||
我们为Windows系统的用户提供了发行版的下载链接,你可以在我们的官网中下载发行版即刻体验。对于Mac OS和Linux系统的用户,暂时不提供发行版,可以参阅“开发者自行安装”一节。
|
||||
|
||||
官网链接:[请点击这里打开](http://www.pyminer.com/)
|
||||
|
||||
### 开发者自行安装(适合Windows、Mac OS以及各大Linux发行版)
|
||||
#### 安装前准备:
|
||||
1. 确认你的Python解释器版本。pyminer支持3.5~3.9。
|
||||
- 当使用Python3.8.x时,建议x>=5,也就是使用Python3.8.5及以上版本,否则安装PySide2可能遇到问题
|
||||
- 3.5.x/3.6.x/3.7.x/3.9.x下,由于开发人员不足,未进行充分测试。为稳定起见,建议解释器版本x>=5。
|
||||
2. 建议新建全新的虚拟环境,尤其是当旧的虚拟环境中存在其他依赖于PyQt/PySide2的程序时。pyminer与这些程序可能发生依赖冲突。
|
||||
|
||||
|
||||
#### Windows安装 pyminer
|
||||
|
||||
```bash
|
||||
#第一步:下载源代码
|
||||
git clone https://gitee.com/py2cn/pyminer.git
|
||||
#第二步:同步安装依赖包和PyMiner,如果遇到安装失败的情况需要手动安装
|
||||
python -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt
|
||||
#安装依赖 (如果下载太慢,请复制源码目录下的 pip.ini 文件到python安装目录下)
|
||||
pip install pyminer
|
||||
#第三步:运行主程序
|
||||
python app2.py
|
||||
在控制台输入 pyminer 回车,即可打开pyminer
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### Linux安装
|
||||
#### 虚拟环境安装 pyminer
|
||||
|
||||
```bash
|
||||
#第一步:下载源代码
|
||||
git clone https://gitee.com/py2cn/pyminer.git
|
||||
#第二步:同步安装依赖包和PyMiner,如果遇到安装失败的情况需要手动安装
|
||||
python3 -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements_linux.txt
|
||||
#第三步:运行主程序
|
||||
python3 app2.py
|
||||
#第一步:创建pip虚拟环境
|
||||
pip install pipenv
|
||||
pipenv --three # 使用当前系统中的python3创建环境
|
||||
#第二步:启动当前目录下的虚拟环境
|
||||
pipenv shell
|
||||
#第三步:为虚拟环境安装pyminer
|
||||
pipenv install pyminer -i https://mirrors.cloud.tencent.com/pypi/simple
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 🌻 项目成员
|
||||
## 开发重点(2021年4月修订)
|
||||
pyminer项目现在的开发目标是,打造初学者友好的Python编程环境,方便更多的人与Python这位踏实可靠(也有点笨呆呆)的自动化助手相知、相熟。
|
||||
|
||||
<a href="https://gitee.com/hzy15610046011" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAFB0lEQVR4Xu1aaU9bRxQ9rAnBmLIaiiG4doIxYAPCrAWrlFZtkCr1Y39Yf0Jb9UNVqVWSpiKhQMtiUwwpEArEgbAYs3shbAGqO8bWM3Hwe+Y9U4mZb8Z3nu+cOffMufNI+va778/AB5I4EEEWcCDOq4EDwYGIFEbOCM4IzoioZoGXBi8NXhq8NC7rJLhGcI3gGsE1gmuEiPsWRcUyR61Gfk4OFlZWcPz2rYh0ri9EUSDqKithNdcgOTkZO14vBp1OLLnXJK02MyMDR8fHigOpKBBfdnRApy1hC9/2evHTk99RmJeHrtYW0AKljMOjY/Q57JhffC1lmuhYxYDQ5OXh84/bkJWZyZJ58dKF3pERlGg0cQGxf3CA3hE7KzMlhmJA1FeZYK2uRkpKCg6PjtDvGMXc4iIKc3PRXGtBxu3bMdeTlpqGrMw7SEpKgn9vD08G/sT69nbMefEEKAbE111dKC4sYDm51zfwc0+P5Pw+aWpEpV7P5m3t7uLHR48lP0PsBEWAMOn1aKmrw630NJycnMAxOYmxqWmxOYXjvurshLZIwz6TyP7a2yv5GWInKAJEt82GuyUfshx2fT487OuH1+8Xm1M47pvuB8jJzmafp+bn0Wd3SH6G2AmyA2G4Wwab1Ypb6eksh+U1D3559gzCRYlNLlYcHck/PHwUK0zU97IDIaTzjQWipuI+mi0WpKWmhnchxIgv2tuRnaUK/z01JYWdBldxnF5/AL8NDIja8VhBsjGC7PQDWweys7IifjMEhPCPFNNt68AHajUD4l/XK/SPjsbKVdHvZQOCzJOhrOydZKMB0WiuQb3JxKw3eYw+h0MxxygWPVmAaKiuQn1VFYjuNM7OzhjthRoRSogc52dtbVCrgo7z4PAQe/v7YvNlceQyh8cnZDVXsgAhLAvaYXKB1HVGA4LKp7wk2H/EOwi4nsEhrHg88T7inXmyAEFPba2vQ829e5hdWGT9RcgICUujyWJGrdHIbPdVxv8aCGqmPirVMspS13kRiCqDAc21tcxtMqZ4PHjc1x8+NYTN2OnpKcamp2F//s9V8JI0VzZGCH9V6CWIEU+HhlgnWlwQ7D0Cb97gjxE7Xrvd4Wk3AghyluQtyGPotFr8PTXFLLNw3BggYvH0RgAx88rF+g+h44wFjJjvo3kUMfOixSREIzgQ593nxMwMyHSlCnqQi7uiVqkiGEP3GL5AAKdn7//vx7XNTdla84QwgsTyshHNX5A7nV1YwNOh4XjZLmnetQNx0V/Q0Up3GaQnxIrply4MJKAhu1YgmsxmmI0V4ZIge/7XmBMlmkLcLy9n/QqVxqrHg5GJ5/BsbUnaZSnB1wJEaXERmi21KMgN9iM06L3F8Pg48xfEBrq/oLjQoJc8rqUlOKdfYMfnk7JGUbEJBYIudU0GA/Jzc5B83p2GnCZZc9KE0CAwOpuboCstjYglhvj8frg3NrC46mbgyDESAsTmzjYq9YZwnxFKnDTAtbyMwTHne1txi9GIOlMl7kR5DyJk0VXBSAgQ1Gt82toCrSZ4NU9NFR19ZLXFvAsldpCWVOh0yFapmHbQqTI5Ny+bkCoChK3RiqL8fLbo0FlPFzLtDQ3w7QUwOTuH1fX1uDaR3pTpSrWgOxDSC7kEVBEg4lrhNU/iQJxvAAeCAxFZi5wRnBGcEVHPJ14avDR4afDSuMy8co3gGsE1gmsE1wgRLT4XSy6WkTT5D8Yz8d8Z2/SVAAAAAElFTkSuQmCC" height="40"/></a>
|
||||
<a href="https://gitee.com/py2cn_admin" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/100/301972_py2cn_1578919767.png!avatar60" height="40"/></a>
|
||||
<a href="https://gitee.com/junruoyu-zheng" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACXElEQVR4Xu2YS0hUURzGvwuZMysxCSKIQEFhUJFMFy2SQEUYY9QBUQtsTBepjELiC0Q0H2DgA12UGQxoEGUQuAiCiKBcmERggohKvjEZJRIfV5iRexoXg8Nw4A4Xxe+sZg6Hw///3e/73XOPEn87xwsOKBTivwsohC8NFIJC+IORjqAj6IiAhwVGg9FgNBiNYF8SZAQZQUaQEWSExH0LYUlYEpaE5ZmBZUttBfKs6aLejU03Gjv6MPFjSgJ1+pecKlhSCN8DpRAUwj/bdAQdQUcEfN8xGowGo8FoBDsMkxFkBBlBRpxrRuTbsuAosCHqUiRW1zeQ+6DqfDqioqQQD4vyEBZ2AUsr6yiva8PiytoJMdoanLBl3RHz87+XkVPs1H/jIrmDIRczjsJclDsKYAq/CPf2XzR3DeDL+OSJEl/2tCL1RoKYn11YhN1RLdmG/mWGCJF26yZaaisRFRkBj8eD0bGPeNL93K/6REssnjbX4OqVy2L+87cJOBs79XcouYMhQmi1uPrbkZxoEWX929mF6/V7DA6/Ff9TkuLx+FExLHExUBQFB6qKZ643GHr1TrIN/csME+Ke3Yqqsvswm02iaq/Xi/0DVfsBkylcCHA8pmfmUFLdhN29ff0dSu5gmBBaPfXOUtizMwQrAg1NHA2SHb0v8P3nL8kWQrPMUCG0ku9mpqHIbkX09Wsw+5ygqof4497Ch09fMTQyaqgTjmU0XIjQPL/Q70IhfJpSCArhHy86go6gIwK+chgNRoPRYDSCnUfJCDKCjCAjyAiJr3bCkrAkLAlLwlIClkc2rHwEe1AdUAAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/lvvl" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACX0lEQVR4Xu2bz4vTQBiG30mbND+6UfGi4EFFEK8rsqAuKx7EP9M/QzytKMIiiuJJFPcoiKLbbdOkaTIyk7jt2rSpwuI68861Lc08fM/7fTOl4uujAwkuCIKoqoAgahsIgiCOByMrghXBimgcFqgG1aAaVGPVSYIZwYxgRjAjmBFr3LcwLBmWDEuG5T8Ly/hBBPdiR3+/zIHkVYr0/WSN6ALcCx30t0M4oQBKYPwuQ/ImW+uzf/OmEw3LeRDq4YofJQ53ExQHZeuzGg0CEsj2cwyfjS0H8QeKmF0RdR2so4ixIMpEwvEF4KiyALJPOYbPlytiLIj8cwHhAd3zdReZAMnr5V3EaBDphwn6Wz5ET2hJiu8lDp82dxGjQQyejBBt+fCve4BioRT5mGP4YlER40GILhA/jOYUkRi9TDWQ+WU8CLXZ3jUX0S0fwpspMthNUA5mg5YVIBSM/u1AA1mmiDUgnNhBfC9E55zqp4DMJIZ7KSb7lSLWgGhSZPqtwODxCHJqGQityN0AvaszRdTpdLSX2lURCkTnjIONnUVF5Lg08xiuJks1RzQtNVeEm6qLVK9OvxRI3mbo3wnMu49YBUIrsh2gd6VWpKwGLfdS1z4QWhHVRc7WXURfZMlq1jDphqqtItS2tSI3fQj3N4FsA7GgyC8eNoJQisT3Q6iB62jZCEIrcsNDtOkD3RqFrSDU9jd2AniX67D430G0XlWfojec6O8ap2ifrY9CEDUigiCI47awIlgRrIjGDkI1qAbVoBqrxktmBDOCGcGMYEa0HsL53/AjROwa7BrsGo2J8RPW2YlZzjPz/wAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/panhaoyu" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAE10lEQVR4Xu2bXWwUVRTH/zM7Ox+7Cy1UsN0KLdvWokCgPhljIjEmJmIMMcKD8kCqBA1qIIaIsWoJxhQFwe/PGlBDNCaGF4gPxBCDiSYYopa0Jf0u21IqJaW7O7OzOzvm3lm3LF2adec6G5N7n/owO3vP7/7P/5xzNxWmz1TY4AsCB+GogIPIZgMHwUHkGyNXBFcEV0TBZoGnBk8Nnho8NeabJLhHcI/gHsE9gntEEfct3CzLZpa+ELTIAViJHpjRw0WclTePeKsIXwiB5k5Iix6g0VnTZ2AM74M1c9abaOf5Fk9ByOEdUJbthiBV5rZkm1EYIx1ITXxVVhiegiCRSpX3Q13xBsRAM7kpdILPGEhNfgt98GXAipUFiOcgaJTEJxrfh7/qYUCQsoHbsGZ+gzG4pyypUh4Q2dCV5W1QwtsB34KcCjLGAIzBNqSnTnqqjLKCIJH6l2yGWr8Xglwz6xupSRhDe5G6/LVnMMoOgmZKxb3QGg5C1IhvOMuKnUOi+wnY5pgnMDwDodS9ClEOO0EmumFG38kLkEAINH8OMbgaGf0C9P4XaHn1ankGItTyC8TASgfE9GnEuzbOiZHAUOvaYE4cRfrqKa8Y0O9hDkKQw7Cta3PKYDEgbhY5SR2lZhukyvuQmjoF/cJTzCExB6HW74Mcfpr2Blb8PIzBl2i+uwERXHMSvoX30ODt5AgSva3MSyxzEFrTh/AvfZxuOpPoQezc3fRvNyCU23ZBWfYiIKqAnUIy+gGSw+1MVcEcRHD1cfgq1jteMPMr4n886BqEqDYgcOc3ELUmB3D8d8T+3MC0C2ULwhdCaO2PELXb6YZTf30PvbfVNQjyArXhbcjVWwGIFIAx9BrMS53MVMEUhFS5HlrTxxDkasBOwxz7CMbQK0xA+Jc8BjVyIDewkc4z0e2kIIvFFIRcsw1qfTsgBol+YQy1wxz/jAkI6jPrTkMMrvtPTJMpCDXyFuSaJ6l87dQk9L7nkJ76gRkIpyI94wxqGQPJ0f1IXjzEQhBs+4jrjZIMT4nzm5Ax+pmBkKo2Qms8BEFaRN/JMj2YKYI6+6rvIKoRp2JM/4R41yO50yqlfJJGyjYnkdF7Z9+Tlx7DSPRspX2K28UMBOkdtEgH4FtIChzM8U4YA7tdgdAa34VUtQHpKydo70CAaI3vwX/rFudSx7oGfWAPUpePueXALjXyytsNRkmNrohZ48ZotOYv4L/l0azCnPkkz5ALAC+VCBtFkP5hzQmIwbVZR58r2VJAXO85qYkvofc9j1yJ9i+GFe+COfYJveZzu5iAIAC0yH74FtwFCHLB6bIUELPl0oZ56SiM/p00Xrl2J6zYWaZjOhMQ/5wGGaOV2h3OSY1/mndI/xaEL9SCwMojEJQ6MmkhOXoQydE33R78TT/PFMR8uwy1/AwxsIo+UsysQC5ylNpnqcLs9FXofbuQvnL8/w8icMcxSIsfmg0ko9M2vOASREAM5K77iwHnlpBnivAv3QJ1xet5P+4UtXnSQUYPIznSUdTjpT7kGQhqctWtkMPbISrLAVGbf892GrZ5EcnxI578RuopiFJPy4vPcRBZyhwEB5GfcFwRXBFcEYV7OP7PbQ4X7hHcI7hHcI+Yr1XnHsE9gnsE9wjuEUVcaHCz5GaZL5O/Adf8pacTFVJYAAAAAElFTkSuQmCC" height="40"/></a>
|
||||
<a href="https://gitee.com/892768447" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/24/74606_892768447_1578916388.png!avatar60" height="40"/></a>
|
||||
<a href="https://gitee.com/fireinice" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/2457/7373699_fireinice_1585479305.png!avatar60" height="40"/></a>
|
||||
<a href="https://gitee.com/houxinluo" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABM0lEQVR4Xu2aMQrCQBQF3xIUxBOIJ1BB8BxCGi9jIwgewdY7WFtZWVjbCtY2NlYhxAi6CoElLgmJEMY6S8jzzex+jRksd6n4yBDEuwUEYWkgCILIipFG0Aga4TwsgAZogAZo5E0SOAJH4AgcgSM8fm9BlsgSWSLLv8lyFY40m/Rf97/eI823Jx0vNw91SeG4p8V0qG47UJyk2hwuWu/PXmuLXFSpLAnCfiUEQRBZOmkEjaARzh0LNEADNH6iUeS091nTqJMlQdhZI0lTRfFDvm+lGCN1WoGMxKzB0CWmz69KGMNtFARBENkNlkbQCBrhPHSCBmg0GI0yg1bdayv9X6PuhylzP4Kw6REEQWRBohE0gkY4NxfQAA3QAI28kyeOwBE4AkfgCI/5HFkiS2SJLPNU8QThDTljz/nugQAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/fzgandw" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/2661/7985556_fzgandw_1598515660.png!avatar60" height="40"/></a>
|
||||
<a href="https://gitee.com/olxjdqj" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABEklEQVR4Xu3YParCQBQG0Cu4BTcguDs38Jpn5WasFBSyigcPXIN9QPzHCJomEMKMRbQ41jMQvtzvOJnB7ndchV8MBPGaAkHUbRCEIJowmggTYSJaDwuqoRqqoRpdXxKMYAQjGMGIhPsWWMISlrD8CixH0yKGo8nzWQ5/iyhXPwmE9bekNywFUb9UQQii2W8TYSJMROt/nmqohmqoRteBmBGMYAQjGJFwbQBLWMISlrDMxLK6niLut4RdEdXlEGUxj9N2nbT+3UUfubzNedjqvI9yM4vj/zJnW/ZaQdSR9RZE9ivqeYMgTERz5EyEiTARrQyrhmqohmp0ndEYwQhGMIIRCV+ysIQlLGEJS1gmJFAveQCBfEScIengwAAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/wfifi" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAGmklEQVR4Xu1ZaWxUVRT+Ztrp0H1f6CYKaQlGwVBJXYCAKCEW454QgyQGNUQNJmgiEdM0YDBRjGsQQzTIDxJRMVLToChCxVSoAaIi1GK6033fp50x97b3eebNvfe9/jV3fnXm3XvfPd/5zneWeko++jsE84HHADHDAgPEbDQYIAwQ4cJoGGEYYRghLRZMaJjQMKFhQkPXSRiNMBphNMJohNEIF/MWI5ZGLI1YGrF0JZaHHynA4gw/X3upYxxbv25xlJiDD+ZjafY8a9251jE8V9nquO/IY4VYlBbD17E944EgVi2I59+bBwJ4saoNTQMB7Tmvr8vBfQsTrDXX+ibx1LFmjAb0s+j3789FaX4c30f3WGJJD24bmsLO76/jcteE8jIlubGoWJuNrPhoa42bfStviMeu1VlIi43CdBA48ns/37/plhREeYGBiSD2nunED/8MK98d5/Pgk4cKsDB1Bkz2cbOvMNmHdzbkoiDZx/d8d20Yr55s539bQDy5NBXPlKTBH+3hqO4724Vvrg4qL0PXi0VjUyF8UNONz/8ccLVPXJ4t3rkqC8l+LwLBEA5f7Mf+8z3KM+65KcFaLxYJUN+t6Xa1b2IqhI9re/HZpb5wIKiH3RwqGMTWtg4FwNBmpDx+ZRC7T3cqL1O+JhtlRYlh1GRfqIepp2QHbS/NsBjU2B/gHvZ6ZsJMF5rbbk/H5mUp8Hk96ByZQvmPHahtGwsHgn2jOlHTMooXvm2TGkSpOTIZ5DTeUJTIX3ClewKbv2xWAkHfQQ3et36+pRP1vZPYdLRJecaHZXlYkRfL2VNVNwTGkPgYL5xCk77DroNhBdVrq7OwcXESPAAa+gPY8lWTVHwoNdnLD5zvwfY7Mnjc945NY8/pTlQ3jkQYsiI/FhVrcpARFwU7NZ8tScOW21I5mN2j0yg/1Y5zLTPeop8lmX7svXc+chOjuS68fbYLW5encVboQpM579DDhViQ4kMwBHxxeQBv/txlHR0GxOM3J+P50gzERnu04kOpKegoPM28dOhCHw7U9kYYQXXFbiwVUTtI9CB6R6H6u9fmcDbpQpM6T6aBYUAsy5mHPetykB0fjcnpED690IeDv0UaJKhJtYTG/pmGEew4cT0CCMo4WQiJtMoMOvbXIM8e9g89Q4QWjX1VaFLGyUIootegtYFMtOzUFKmOeltVC4izVZ6jKVxVywjmUdZQb6tCk54tE9UIIF6+OxOPLknmKixDV0ZNRjWadZiAvvVLFyqvDlkOpWxTxTIFU+Y1Gj5U9al4q0JTsE2VESOAKCtOxEt3ZnIVtqcYZpWMmsJa4XGZGFEAVeruBCalt50x1OP20KQAqgqvCCAo9WWek1FTAEHZZL8ofabL9+J8medE+pMBrQtN+kxVikvnEUIM7S9UUVMAoWPT/o15PHycijXKOOpZWh7LQk/HJirkqmJNCgRNj6zy2nZ8ppHSUZM9p5elYqYSWFnFREOI1jIUZCcxtjtQx2JxBykQ6xcl4pWVmUiI8YZ1gyIOZdQUB9LqrbJuCBWnOuDGCLGfiirNANQ5qvRMw084kDJFV+xJgaCeHZ4M4o3qLlQ3Dlv9gIyawhDKGpF16AVVRlB2CNGltQwtq1UFGwVcCHJJbpzVTOrKf+XMUnhWdIN1PRNWx6ebGdCc3jEyhV0n2/F0SbrVG6iMoEBQ4Birjv7Rb5XVOq/SEBQOZD0JaxvYR9cQKoGg1RrzIhuUiJmB24aM1Rfv1XTjiVtTeC+gM4IC8UBxEnbclQlWHzAvMgNE6T+Xhoy188vzYvnwyGlEoASCepa9vG0wwOt5J9VnBtEWvap+CCzbsFmDU2cqwKCeZaz6tWUUZUVJvMibS4v+U8MIitL9vEFz6kyVQNBqjRVWgSCQN9vxOU2QaN6u65nEjak+RHs9jrMKygqhCYzibN5RnO6P6FhlWYc6kGUdNkFjtjjNKrT/16CenQqF4I/yhM35ZBdhv1GlHpwIIsnvdaSm/SyRJdjv4t2ySte+jzqQhaYvCvDCw0eCuumVFgiaAcQLnahpL7fFdyGcF9vHVfiF/U4zgHjgdqhsH+wK4TxR/1/vY7+EFghaSbKNujmB/WCq/OyZWyPEOfZBq652sb/bPk91Mxl3/JcfHa25oaa4FPXoXIygRtHiTFe72IGwT9jd1C6OQLji8f9gkQFi1okGCANEeDwbRhhGGEZIc5wJDRMaJjRMaOgKYKMRRiOMRhiNMBrhYkxgxNKIpRFLI5ZGLF2I5b+O07VathbMeAAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/BigBossQzz" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/1760/5280558_BigBossQzz_1598407750.png!avatar60" height="40"/></a>
|
||||
<a href="https://gitee.com/gavinee" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAG4ElEQVR4Xu1aaWxUVRT+ptNlBoYpbZluFLpBCxJkKbIviiKLFQPWslkVQiQYCBaiKEjAgAgmQhCUmBgKElG2QrBYI5GCCpbKZlhK9710X6admU6nMzXnljZv2mk70DuVH/f86uS+d9+53/3Od849t7Jnr8c2QxhkAogWFgggHkWDAEIAYS2MghGCEYIRNosFERoiNERoiNDo6iQhNEJohNAIoRFCI+zotzhULA8OXYVRqiBkGUrwW/VtnC5Pht5itOnWfK/nML7fUDaWaXiIw6VJdrjP7xGHAbHafzaW+86Em8wFpmYzjpQmYX/RL516vjVwERYOmMDGr9Vl4N30gx2eDVH4QC5zQobhIT8EHJk1pqiHYWtQNHxc+sMMC5Kq72JL7o+YqA7DCt+ZCFX64ljZn1bAdAZEHyc3RHpF4LUB4xGuHIg6swH7ChNwtjKFKxjcGUG7tjskBmFKfzSjGde0GYjNimNOfxIYhTmeYyCHExosJnxfeglfFyeyMSkQN+uzkW4oxhhVCIIUGsYqqZU01mBn/mlcrr3HDQyuQNDu7Q1djgnqlli/VZ+D7Xknkd1Qyn5Lx2WQtexu0XmcLL9qBYSt1RGzyhprcaM+G2cqknG9LosbCDQRNyAmq8OxLiAS4Up/0CKNzSbozY1wdXJmDtOuOsucOjhPO78x+yhifJ5v0wh6iBZe12RAuuEh/tam4VLN3TZAuSLAWyM2D45ClGYSnCB7LD8pfP6ovY9KU30bEBQaBE6ZqbbDXGNVIVjiPRWJVbdwsebOY32rq4e5MWKu51hsGrwQankf9r2mZgtjRb3ZgNomPapM9Sg3aaE163FfV4AJ6jC84hXBgCswViJVX4jZHqO7zBo0OMtjFLYEvgG1XIn7+kIsTd3LBQxuQFD8v+c/BzkNpUwbWnWhMy8D3TTYMGg+i3USTalY3tHlY31WnE1GbBy0ANGayXCWyXFVm4bVGd8+XUC0ehM/4kOEKnztco6ovznnB6TUZWKV38tY6fcSXGUtmtKdNTWbGYAktjyMGyN6CgSl3V0s7foxse3OWkW2O+Z1N0/ruMOAoFL6cEkSioyVVr4MdPPCO74vsFQqZcSIPoMQrPCGr6sHghTenfpvgQV3dflIqLzRablu7+KlzzkMCJ3FiJ15p5BQdcPKr0jPCGwKjELfdkDYU2ITeCq5wqZ2PMniewUIexyTMqIrIMb1C8WCARNBtQplmQPFiawI42kOYwTVB1RGW2D9Xwe0EIWTC9OBroA4U3GNpcoxqmB4OPe10g1pRcoLDIcB0ZPQIPBsFWZUbeY2lOFsRQrLGDztqQRCukBHnzEcnjXs2a3OQoPYRD2HFG0GEqtudluc2fOt7p5xGCN6qhG2GjOtizk2PBY6sxEXqm9zS6MOA6InGtFZh4qAoObOjqCl0LioUdVUj225x7n0JXoFiGnuw+H+6DAWrPTBMu/pUDq5WmWNjwcvRLRmChNJg6WRFWLtMw4BoZIr4efan2WRnIYydujqrA/aXTj0Sh0hZcT+ISsx3f2ZDn5lNpQgJnUfWwiBEBsQySpOe4xASqi8zlqAPKxXGLFYMxXvB0QyFpBRfVFgrEBcyUWcl1Se6wNexTzPCHg4q2w2cehd0h7Sh5S6DHyeH8+tyuQORGsYUD+CGiy2mis8dpD3HNyBIAe3By1hVWFxYxWStek4UJTYIY6pH5FnLLdaD4E4SR2OB/oinKv8h41RqLyumQhq/NA7P5X/1eW1wJMCxB2IkX0D8UXIW/B39WA+URtubeZ37O8NAfOx1Hs6o/1tXS7efvBVm99x4WtAbTgy6lYtTt3D/qb5tgZGY6jSj/12RHlN83IHgtgQ6TWOqT85vafwZ8RXJLNFSJsvRY1V+CDrCO7pC9gYdbdW+L4IF5mctfN25sezYopsZv+RrD3n6axiv0tNNfg09wSuaB88KQE6vMcViPYOS9lAX5YewWvNetbqv1D9L3NqfL8h+Cx4Gbxd3Jkg0nliW97xNofXDpyHGJ8ZbXccJJbrMg9xSZ1cGSG92KGJSSSp2JHu2gz3EdgWtIjtLKXM3flnrG6srMOjCCvSDlgtlO5SJ6nDWA1BrTq6Lfuy8BwXVnBjxK7gNzHLYzSLf+peHy293EHUpLtO96GHSn7HN8W/ti1EWlRRx3tH/il2n9Fq0vfpMHZPV4CPso+Cwqynxg0IcoQaKEs001Bj1jHatzdqx1GbzmhpYgep9pc29P5k9TAka9NYQ9eWrfGfi8EKDU6UX+F628UViJ7uyv/5vgDiEfoCCAGEdSAKRghGCEbYTE4iNERoiNAQodFV5So0QmiE0AihEUIj7DjfC7EUYmlNk/8AnJ4R08wDRU0AAAAASUVORK5CYII=" height="40"/></a>
|
||||
<a href="https://gitee.com/wudenggang" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAE0klEQVR4Xu2Zb2xTVRjGn9u1naUbXec22MZgCImIMYgOXIjbwj8nCSSAxoQPSDREQ6bRRE0kYsiCZh+URBINShYI8IEEUIyMGBEhIJpFR4AYEFAMbqx06+zWlXZ0/UfOHbeee7337pbe6/rh7bem99xzzvO+7+99zqlQ9/kfadAHAgkxlgUkxL1qICFICDkYKSMoIygjVM0ClQaVBpUGlYbeSYIYQYwgRhAjiBEG7lsIlgRLgiXBkmD5f8Fy6UNF2NxYAU+hDckUcOC3IezoHNCdvmGGG1uaKlDqKhCfi6fS2Ht+EF90BXXHNc8uxrsN5Shy2gyPMaCDOZe30z0OfLKiCjUehzjnmRsRvPXdLd35X60rxYb5XjhsQuY5I+PeqC/DusdKUGADgiNJfHC6Hz/+HTGyV91nTGuf25sr0VjrFie7MRTHhq+6EY1r/2XCPy+tsCcUx5vf+tAdimsumh93ZSCG9V/25CwCe4FpQvARHi9SfAYNx1JIpNJiiURGU/j45wA6roZVNzfJIWDv2umoLXGASXz0yjC2ne7PLyF4Towm09hzfhDt59TrfeXDxXh7UTncThuuD44iGE1iQbULqTRw+HIIH50NqG6On2MkkcannQM4eCmUX0KwaO1eU4NZXqe4sI5rYbSe6lNd5DtPl+P5uR4wPDAusFKQ6v5i3x1s/Pqm6rhNCx7E+sdLRK74wgls/v4WLgdi+SUEW82Hy6bimVlF4sL06rd99TTMm/JAhvp/BkczXac/ksDWk33o8o38Z4M8H9jvm472miKCqYxgL9v4ZClemu+Fs0BAXySBLSf8uOC/I1tsXZULrUumoMJtz1D/nC+aqf1YIo1dXUHsuzgoG8dn3HgldD/qmAZLNjnvDbRq+MV5XrxSV4pCuyDLGj7ax6/fxnsn/LL98O8eD6oTLgRbwP7najCnrFATfFL5KKnPdx0G0JeP9MjaL59tRtpstmKYmhFs8vebKrBqzmQwm/RL7whaOv6tYz69lRnDRzwUS6HtTD9++Ot2Zj88f4wYrwkXgk99ZeT49qdGfSmb1Gy69JtRKz7hQvAwVEaWt8fKbNHLpoXTXGhdPBVlkwpMtdW8WKaXBns53x73XxjCzl//Eef8bGU1Fla7NA9mLzzqwWv1ZXDZ5T5BC7DZRl3veUuEYCfRNY+McULqALytVmMAW+Tc8kK0La9EVbFdBOX2nwL45uqwjDtqHcUMQSwRgo8sM0vrDnVDaauVXUHajJQ1vFeQ+KDlMfJWCD6yA9Ektp7yo6m2KGOr9aLK2+/Om1HxjkIyYHquM1cxLMkItqidq6rBwClFsXGmW7TV2RzI2HH+8KUhtDw1xg29c0jeCiFFVhCAY9fCeKLSJdZ+Nkd09izLimdnF4v71DuZ5q0QPBN+D8Qwo8QJZqiMXKZIdpuZLn84jple57h3FXkrBN8lYsk07IIAm83YZYpkt9nm4kmIAlphqy33EcoOIH03epmivNhl41mJvH7Ml2vgNcdbBks2I+8k2fdsLlOklsnGGb0Zz0UlS4XgzxZskWq2Wmvx/OFNy4DlsnHlWEuFMHOhVr+LhLinMAlBQsiLjTKCMoIyQrUBUWlQaVBpUGnouVNiBDGCGEGMIEYYOMMTLAmWBEuCpR4q7gJAqTY8I+99qgAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/py2cn_admin" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABOElEQVR4Xu2ZMQrCMBSGX2/i5uwFPIGjB9BzuHoCV++gm5O6OnZTEGcLnQQXwWoFDdJgDG+oScGva0Ob/vn/L695SXs2KIVLEoR4uQAhTBoQAiFsMOIIHIEjnMUC0SAaRINo+P4kYASMgBEwAkYozluAJbAElsCykbAcd4bSb3U/5na9FzLdL2SymysQV9+QaLBECLOIVSHyy0k2+fZ5pyhvsspSWWdpfcuteFIjHHE4H6W3HCmm+7shCBG7jqhGA0eYXQMhEEKEaDi2T6JBNIjGuyKCETDCLo9xBI7AEc5fRqJBNIgG0fCdJ8EIGAEjYASMUJy6A0tg+R2WdLpoAttHdVWv/F03XMHToEOi9T6DfqXiZQgRuwmsWKSgQ3AEjrANhyNwBI5wQphoEA2iQTR8FRqMMOo8ACOBabofxKKyAAAAAElFTkSuQmCC" height="40"/></a>
|
||||
<a href="https://gitee.com/nwljy" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACX0lEQVR4Xu2bz4vTQBiG30mbND+6UfGi4EFFEK8rsqAuKx7EP9M/QzytKMIiiuJJFPcoiKLbbdOkaTIyk7jt2rSpwuI68861Lc08fM/7fTOl4uujAwkuCIKoqoAgahsIgiCOByMrghXBimgcFqgG1aAaVGPVSYIZwYxgRjAjmBFr3LcwLBmWDEuG5T8Ly/hBBPdiR3+/zIHkVYr0/WSN6ALcCx30t0M4oQBKYPwuQ/ImW+uzf/OmEw3LeRDq4YofJQ53ExQHZeuzGg0CEsj2cwyfjS0H8QeKmF0RdR2so4ixIMpEwvEF4KiyALJPOYbPlytiLIj8cwHhAd3zdReZAMnr5V3EaBDphwn6Wz5ET2hJiu8lDp82dxGjQQyejBBt+fCve4BioRT5mGP4YlER40GILhA/jOYUkRi9TDWQ+WU8CLXZ3jUX0S0fwpspMthNUA5mg5YVIBSM/u1AA1mmiDUgnNhBfC9E55zqp4DMJIZ7KSb7lSLWgGhSZPqtwODxCHJqGQityN0AvaszRdTpdLSX2lURCkTnjIONnUVF5Lg08xiuJks1RzQtNVeEm6qLVK9OvxRI3mbo3wnMu49YBUIrsh2gd6VWpKwGLfdS1z4QWhHVRc7WXURfZMlq1jDphqqtItS2tSI3fQj3N4FsA7GgyC8eNoJQisT3Q6iB62jZCEIrcsNDtOkD3RqFrSDU9jd2AniX67D430G0XlWfojec6O8ap2ifrY9CEDUigiCI47awIlgRrIjGDkI1qAbVoBqrxktmBDOCGcGMYEa0HsL53/AjROwa7BrsGo2J8RPW2YlZzjPz/wAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
<a href="https://gitee.com/tangtang666" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABHUlEQVR4Xu3YO4qEQBhF4b8TXYzL0NjFmAiGrkvBVbgVE2Ga7jERfMEg3QOfqWVRXM89VvmIiJ9wxUMQvxQIYmmDIASxFiMiEIGIzc2CaqiGaqjG0UmCIziCIziCIy78byFLsiRLsvyYLNu2jbquI0mSC7o6HtL3fRRF8ed59ia4VZaCWGJvmiaqqtol4kVKmqbv0fM8xzRNu298GIYoy/J/EnG26q7rIs/z97BxHCPLsrNHbrt/azXOVi2IJSFBCGJdFkQgAhGbHxDVUA3VUI2j3SVHcARHcARHnJ3BI4IsyZIsyZIsL8jym4Z89OetIL4pgWUtiBDEGktEIAIRm6pWDdVQDdU42sdxBEdwBEdwxIXTLlmSJVmS5ZEqnjhi2AGX5AhBAAAAAElFTkSuQmCC" height="40"/></a>
|
||||
<a href="https://gitee.com/dechin" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAC0UlEQVR4Xu2YT0iTcRzGn70zKVthWVJzEVSrKPs7EoJEMoLw0KUunaNDlzoEHurSqUM3CyIQ6c8pqFskQbYKp1iWaeqWoo1NWzrX3NpwOLe98b66cLnGhv7eXtjzXt7D3r3s+9nzfN739zM0X2iVwQMGgphPAUEstIEgCCJTjEwEE8FEZH1ZYDVYDVaD1ci1kqAj6Ag6go6gI/LYb6EsKUvKkrL8b7JsuF4Dc3VFHqoCkvEU4rEEAu4wvtrH4Pk4mdf3VuoiobIsBETGQDLgHw2h67EL/pHQSs2a8z6agZCTMhLxZPZ+SgaUlBoBQ+bH0UAMjpZBjPdNCYehGYjQeBTPGtv/OdDajathra3CnpMWrNtc9gdK0BuB/W4vQt+jQmHoBkR6SgVI3eWDMO+rUGHIMjDa4cPbe33FBUKZtrzKhFNXj2CDxaQOHwvH8e7+F6EV0V0i0n/7obM7cPScFcZVkpoK1ysPOh86haVCtyDWbynDmcZjUM7KMTk8jec3u4oPhDLx6Ws2bLdVqsNHpmJ43fQZgW9hITB0mwhl2hMXq7G3fpsqzdnoHBwtA3C/nyg+ELbzViiukEokzMUS6HgwiBGHjyAIgolgNVQP1F46oL5yp1+q2pv74e3xF58jGm7UwLx/fhn/a2IGL293q2cRh24fn5W7ylF/5TBMm9aoc/sGfqL11gcRDNR76hbE4kdnKimj/4Ub3U+GigvE34suZV/CfqdX6CaN7hKxZBmekuFq8wpdcOmqGkoKdtdZsPP4Vigw0rtVQU8EbU09wiSZ7ppmici1VScZJRhLpSX9V54QnY+cQvchNAdRiOUUaD5nEJ+eDgv1wuLfpFkicoKQoW7szkzPqoMPvRnDD1ewEHbLvlYoiGX/Og1vQBALsAmCIDJ7x0QwEUxE1mcRq8FqsBqsRq4XVTqCjqAj6Ag6Io/lPGVJWVKWlCVlmYcsfwMrVQjz8fzYbAAAAABJRU5ErkJggg==" height="40"/></a>
|
||||
因此pyminer的开发重点为:
|
||||
|
||||
## 📱 联系我们
|
||||
1、计算、统计方面低代码化功能性开发。需求详见:[低代码化功能性开发](https://gitee.com/py2cn/pyminer/issues/I3HTG9?from=project-issue)
|
||||
|
||||
作者:PyMiner Development Team
|
||||
2、编写适用于Python入门的pyminer官方教程。需求详见:[编写教程](https://gitee.com/py2cn/pyminer/issues/I3I7FW?from=project-issue) 。在教程编写方面,
|
||||
我们深知开发团队力量有限,因此诚挚希望可以和各位开发者或培训机构合作。
|
||||
|
||||
邮箱:aboutlong@qq.com
|
||||
3、插件商店的开发。插件商店可以让pyminer如虎添翼。这一部分的开发需求详见:[插件商店](https://gitee.com/py2cn/pyminer/issues/I1TWAR?from=project-issue)
|
||||
|
||||
为了减少开发负担,样式表只使用浅色样式表(Fusion),不对深色样式表做优化,且不再进行界面语言翻译工作。
|
||||
界面语言直接使用中文。
|
||||
|
||||
## 📱 加入我们
|
||||
|
||||
作者:pyminer Development Team
|
||||
|
||||
邮箱:team@pyminer.com
|
||||
|
||||
欢迎各位开发者大佬加入
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
<img src="pyminer/resources/screenshot/group.jpg" width = "300" height = "500" alt="QQ群" align=center />
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
|
||||
|
||||
## 🚥 许可说明
|
||||
本项目遵循GPL许可证。此外,对个人用户来说,本项目支持自由修改源码和使用,但是不支持任何未经授权许可的商用或其他盈利性行为,也不支持任何未经授权申请著作权的行为。如有违反以上许可,本项目管理团队有权进行否决。
|
||||
许可解释权归属 PyMiner Development Team。
|
||||
本项目遵循LGPL许可证。
|
||||
|
||||
许可解释权归属 pyminer Development Team。
|
||||
|
||||
## 📸 预览截图
|
||||
|
||||
基本界面
|
||||
![avatar](pyminer2/ui/source/screenshot/main.png)
|
||||
|
||||
查看变量
|
||||
![avatar](pyminer2/ui/source/screenshot/check_data.png)
|
||||
![avatar](pyminer/resources/screenshot/main.png)
|
||||
|
||||
代码提示
|
||||
![avatar](pyminer2/ui/source/screenshot/code.png)
|
||||
![avatar](pyminer/resources/screenshot/code.png)
|
||||
|
||||
绘图
|
||||
![avatar](pyminer/resources/screenshot/check_data.png)
|
||||
|
||||
|
28
app2.py
28
app2.py
@ -1,28 +0,0 @@
|
||||
import cgitb
|
||||
import time
|
||||
import logging
|
||||
from pyminer2.pmappmodern import main
|
||||
import ctypes
|
||||
import platform
|
||||
|
||||
if platform.system().lower() == 'windows':
|
||||
"""
|
||||
无论是点击bat还是在终端运行,如果你发现控制台窗口闪现一下就关闭了,那么就是由以下代码造成的。
|
||||
注释掉以下代码即可正常显示控制台。
|
||||
"""
|
||||
whnd = ctypes.windll.kernel32.GetConsoleWindow()
|
||||
if whnd != 0:
|
||||
ctypes.windll.user32.ShowWindow(whnd, 0)
|
||||
ctypes.windll.kernel32.CloseHandle(whnd)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t0 = time.time() # 开始时间
|
||||
# 异常处理设置
|
||||
cgitb.enable(format='text')
|
||||
|
||||
# 日志设置
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
t2 = time.time() # 结束时间
|
||||
logging.info(f'time spent for importing modules {t2 - t0}') # 打印日志,启动耗时
|
||||
main()
|
4
docs/.gitignore
vendored
4
docs/.gitignore
vendored
@ -1,2 +1,6 @@
|
||||
/build
|
||||
/source/alg
|
||||
/source/pkgs
|
||||
/source/pmg
|
||||
/source/pyminer
|
||||
/source/dev
|
7
docs/PyMiner介绍.md
Normal file
7
docs/PyMiner介绍.md
Normal file
@ -0,0 +1,7 @@
|
||||
# PyMiner——Python数据分析的利器
|
||||
对于Python初学者而言,从安装Python解释器到配置专门的代码编辑器,无疑是十分耗费精力的。
|
||||
|
||||
PyMiner是一款使用PySide2编写的数据分析软件,内部集成了数据分析的常用包,并且将常见操作封装为图形界面的形式,提供低代码的数据处理交互。
|
||||
|
||||
在线文档可以看这里:
|
||||
[在线文档](https://gitee.com/py2cn/pyminer/wikis/%E7%AE%80%E4%BB%8B?sort_id=2761029)
|
152
docs/make_doc.py
152
docs/make_doc.py
@ -1,56 +1,114 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
# 文档的生程流程如下所示:
|
||||
# 首先,根据*.py文件生成一系列的*.rst文件,然后根据*.rst文件生成html文件,即静态页面。
|
||||
base_path = path.abspath(path.join(__file__, '../..'))
|
||||
sys.path.insert(0, base_path)
|
||||
|
||||
# 是否将文件上传
|
||||
# 在进行页面上传时,会开启所有的强制刷新选项,这会降低页面的生成性能,但无伤大雅。
|
||||
upload = False
|
||||
# upload = True
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
# 项目的根路径,即pyminer项目的根文件夹
|
||||
base = os.path.dirname(os.path.dirname(__file__))
|
||||
sys.path.insert(0, base)
|
||||
|
||||
# *.py文件夹,在这些路径读取*.py,然后生成*.rst文件
|
||||
alg_src_dir = os.path.join(base, 'pyminer_algorithms')
|
||||
|
||||
# 用于存储rst模板的文件夹,这些模板用于生成*.rst文件
|
||||
template_dir = os.path.join(base, 'docs', 'templates', 'apidoc')
|
||||
|
||||
# 根据*.py生成的*.rst文件夹
|
||||
alg_rst_dir = os.path.join(base, 'docs', 'source', 'alg')
|
||||
upload and os.path.exists(alg_rst_dir) and shutil.rmtree(alg_rst_dir)
|
||||
|
||||
# make文件的路径,本脚本调用sphinx的makefile实现功能
|
||||
# TODO 目前仅支持调用windows下的makefile,请linux用户自行优化脚本并提交
|
||||
make_path = os.path.join(base, 'docs', 'make.bat')
|
||||
|
||||
# 最终生成的html文件夹
|
||||
html_path = os.path.join(base, 'docs', 'build', 'html')
|
||||
from pyminer_devutils.doc import RstGenerator, PythonFileTreeNode
|
||||
|
||||
|
||||
def cmd(command):
|
||||
for line in os.popen(command):
|
||||
print(line, end='')
|
||||
def main():
|
||||
# 在这个文件下定义了文档的具体生成细节,包括rst文件夹的位置、排除的文档等。
|
||||
base = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
source_dir = path.join(base, 'docs', 'source')
|
||||
rst_root_dir = path.join(base, 'docs', 'build', 'rst')
|
||||
path.exists(rst_root_dir) or os.makedirs(rst_root_dir)
|
||||
|
||||
generator = RstGenerator()
|
||||
|
||||
generator.sync_directory(source_dir, rst_root_dir, rm=False)
|
||||
|
||||
# 生成算法部分的文档
|
||||
py_dir = os.path.join(base, 'pyminer_algorithms') # py源文件的目录
|
||||
rst_dir = os.path.join(rst_root_dir, 'alg') # 根据py生成的rst文件的目录
|
||||
exclude_dir = [os.path.join(py_dir, p) for p in (
|
||||
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
|
||||
'plotting/graph.py',
|
||||
)]
|
||||
node = PythonFileTreeNode(py_dir, exclude_dir)
|
||||
generator.generate_python_package_files(node, rst_dir, 'pyminer_algorithms')
|
||||
|
||||
# 生成界面部分的文档
|
||||
py_dir = os.path.join(base, 'pmgwidgets') # py源文件的目录
|
||||
rst_dir = os.path.join(rst_root_dir, 'pmg') # 根据py生成的rst文件的目录
|
||||
exclude_dir = [os.path.join(py_dir, p) for p in (
|
||||
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
|
||||
'display/browser/get_ipy.py',
|
||||
'widgets/basic/texts/webeditors/__init__.py',
|
||||
'examples',
|
||||
'display/browser/handler.py',
|
||||
'widgets/basic/quick/demo1.py',
|
||||
'widgets/basic/texts',
|
||||
'widgets/extended/texts/texteditor.py',
|
||||
'utilities/platform/test',
|
||||
)]
|
||||
node = PythonFileTreeNode(py_dir, exclude_dir)
|
||||
generator.generate_python_package_files(node, rst_dir, 'pmgwidgets')
|
||||
|
||||
# 生成主程序的文档
|
||||
py_dir = os.path.join(base, 'pyminer2') # py源文件的目录
|
||||
rst_dir = os.path.join(rst_root_dir, 'pyminer') # 根据py生成的rst文件的目录
|
||||
exclude_dir = [os.path.join(py_dir, p) for p in (
|
||||
'extensions/packages', # 插件文档独立生成,这里跳过。
|
||||
|
||||
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
|
||||
'ui/pyqtsource_rc.py',
|
||||
'ui/base/widgets/resources.py',
|
||||
'core',
|
||||
'extensions/package_manager/package_manager.py',
|
||||
'features/io/database.py',
|
||||
)]
|
||||
node = PythonFileTreeNode(py_dir, exclude_dir)
|
||||
generator.generate_python_package_files(node, rst_dir, 'pyminer2')
|
||||
|
||||
# 生成插件的文档
|
||||
py_dir = os.path.join(base, 'pyminer2/extensions/packages') # py源文件的目录
|
||||
rst_dir = os.path.join(rst_root_dir, 'pkgs') # 根据py生成的rst文件的目录
|
||||
exclude_dir = [os.path.join(py_dir, p) for p in (
|
||||
'ipython_console/initialize.py', # IPython控制台的全局文件,没什么生成文档的意义,跳过
|
||||
|
||||
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
|
||||
'pm_preprocess/ui/pyqtsource_rc.py',
|
||||
'code_editor',
|
||||
'data_miner',
|
||||
'cftool',
|
||||
'extension_app_demo',
|
||||
'pm_preprocess',
|
||||
)]
|
||||
node = PythonFileTreeNode(py_dir, exclude_dir)
|
||||
generator.generate_python_package_files(node, rst_dir, 'packages')
|
||||
|
||||
# 生成开发工具文档
|
||||
py_dir = os.path.join(base, 'pyminer_devutils') # py源文件的目录
|
||||
rst_dir = os.path.join(rst_root_dir, 'dev') # 根据py生成的rst文件的目录
|
||||
exclude_dir = [os.path.join(py_dir, p) for p in tuple()]
|
||||
node = PythonFileTreeNode(py_dir, exclude_dir)
|
||||
generator.generate_python_package_files(node, rst_dir, 'pyminer_devutils')
|
||||
|
||||
print('RST文件结构已生成。')
|
||||
|
||||
app = Sphinx(
|
||||
srcdir=rst_root_dir,
|
||||
confdir=rst_root_dir,
|
||||
outdir=path.join(path.dirname(__file__), 'build/html'),
|
||||
doctreedir=path.join(path.dirname(__file__), 'build/doctrees'),
|
||||
buildername='html',
|
||||
confoverrides=dict(),
|
||||
status=sys.stdout,
|
||||
warning=sys.stderr,
|
||||
freshenv=False,
|
||||
warningiserror=False,
|
||||
tags=[],
|
||||
verbosity=0,
|
||||
parallel=0,
|
||||
# parallel=multiprocessing.cpu_count(),
|
||||
keep_going=True)
|
||||
app.build()
|
||||
|
||||
|
||||
# 在上传时,强制重新生成*.rst文件,否则可以根据缓存加速rst文件生成过程
|
||||
if upload:
|
||||
cmd(f'sphinx-apidoc -fMeo "{alg_rst_dir}" "{alg_src_dir}" -t {template_dir}')
|
||||
else:
|
||||
cmd(f'sphinx-apidoc -Meo "{alg_rst_dir}" "{alg_src_dir}" -t {template_dir}')
|
||||
|
||||
# 在上传时,删除已有的html页面,并重新生成,否则可以利用缓存加速html生成过程
|
||||
if upload:
|
||||
cmd(f'"{make_path}" clean')
|
||||
cmd(f'"{make_path}" html')
|
||||
|
||||
# 进行单元测试
|
||||
cmd(f'"{make_path}" doctest')
|
||||
|
||||
# 在上传时,将生成的html文件推送至gitee的pages仓库
|
||||
if upload:
|
||||
cmd(f'ghp-import -npfr git@gitee.com:py2cn/pyminer -b pages "{html_path}"')
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
1
docs/source/_static/css/README.txt
Normal file
1
docs/source/_static/css/README.txt
Normal file
@ -0,0 +1 @@
|
||||
关于样式,采用了SASS预处理器,以使用一系列的高级功能。
|
13
docs/source/_static/css/pyminer.css
Normal file
13
docs/source/_static/css/pyminer.css
Normal file
@ -0,0 +1,13 @@
|
||||
@import url("../pydoctheme.css");
|
||||
img {
|
||||
border-radius: 2px;
|
||||
border: solid 2px #dddddd;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.important {
|
||||
background: #FFC107;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=pyminer.css.map */
|
1
docs/source/_static/css/pyminer.css.map
Normal file
1
docs/source/_static/css/pyminer.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["pyminer.sass"],"names":[],"mappings":"AACQ;AAER;EACE;EACA;EACA;EACA;;;AAEF;EACE","file":"pyminer.css"}
|
11
docs/source/_static/css/pyminer.sass
Normal file
11
docs/source/_static/css/pyminer.sass
Normal file
@ -0,0 +1,11 @@
|
||||
//noinspection CssUnknownTarget
|
||||
@import url('../pydoctheme.css')
|
||||
|
||||
img
|
||||
border-radius: 2px
|
||||
border: solid 2px #dddddd
|
||||
margin-top: 10px
|
||||
margin-bottom: 10px
|
||||
|
||||
.important
|
||||
background: #FFC107
|
9147
docs/source/_static/index_files/all.css
Normal file
9147
docs/source/_static/index_files/all.css
Normal file
File diff suppressed because one or more lines are too long
22
docs/source/_static/index_files/all.js(1).download
Normal file
22
docs/source/_static/index_files/all.js(1).download
Normal file
@ -0,0 +1,22 @@
|
||||
/*1606211680,,JIT Construction: v1003023083,en_US*/
|
||||
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
|
||||
* copy, modify, and distribute this software in source code or binary form for use
|
||||
* in connection with the web services and APIs provided by Facebook.
|
||||
*
|
||||
* As with any software that integrates with the Facebook platform, your use of
|
||||
* this software is subject to the Facebook Platform Policy
|
||||
* [http://developers.facebook.com/policy/]. This copyright notice shall be
|
||||
* included in all copies or substantial portions of the software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
(function _(a,b,c,d,e){var f=window.console;f&&Math.floor(new Date().getTime()/1e3)-b>7*24*60*60&&f.warn("The Facebook JSSDK is more than 7 days old.");if(window[c])return;if(!window.JSON)return;var g=window[c]={__buffer:{replay:function(){var a=this,b=function(d){var b=window[c];a.calls[d][0].split(".").forEach(function(a){return b=b[a]});b.apply(null,a.calls[d][1])};for(var d=0;d<this.calls.length;d++)b(d);this.calls=[]},calls:[],opts:null},getUserID:function(){return""},getAuthResponse:function(){return null},getAccessToken:function(){return null},init:function(a){g.__buffer.opts=a}};for(var b=0;b<d.length;b++){f=d[b];if(f in g)continue;var h=f.split("."),i=h.pop(),j=g;for(var k=0;k<h.length;k++)j=j[h[k]]||(j[h[k]]={});j[i]=function(a){if(a==="init")return;return function(){g.__buffer.calls.push([a,Array.prototype.slice.call(arguments)])}}(f)}k=a;h=/Chrome\/(\d+)/.exec(navigator.userAgent);h&&Number(h[1])>=55&&"assign"in Object&&"findIndex"in[]&&(k+="&ua=modern_es6");j=document.createElement("script");j.src=k;j.async=!0;e&&(j.crossOrigin="anonymous");i=document.getElementsByTagName("script")[0];i.parentNode&&i.parentNode.insertBefore(j,i)})("https:\/\/connect.facebook.net\/en_US\/all.js?hash=1f66f14b00fabb1f6f091c9348f79c88", 1606211680, "FB", ["AppEvents.EventNames","AppEvents.ParameterNames","AppEvents.activateApp","AppEvents.clearAppVersion","AppEvents.clearUserID","AppEvents.getAppVersion","AppEvents.getUserID","AppEvents.logEvent","AppEvents.logPageView","AppEvents.logPurchase","AppEvents.setAppVersion","AppEvents.setUserID","AppEvents.updateUserProperties","Canvas.Plugin.showPluginElement","Canvas.Plugin.hidePluginElement","Canvas.Prefetcher.addStaticResource","Canvas.Prefetcher.setCollectionMode","Canvas.getPageInfo","Canvas.scrollTo","Canvas.setAutoGrow","Canvas.setDoneLoading","Canvas.setSize","Canvas.setUrlHandler","Canvas.startTimer","Canvas.stopTimer","Event.subscribe","Event.unsubscribe","XFBML.parse","addFriend","api","getAccessToken","getAuthResponse","getLoginStatus","getUserID","init","login","logout","publish","share","ui"], true);
|
5523
docs/source/_static/index_files/all.js(2).download
Normal file
5523
docs/source/_static/index_files/all.js(2).download
Normal file
File diff suppressed because one or more lines are too long
182
docs/source/_static/index_files/all.js.download
Normal file
182
docs/source/_static/index_files/all.js.download
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/source/_static/index_files/check_data.png
Normal file
BIN
docs/source/_static/index_files/check_data.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 246 KiB |
BIN
docs/source/_static/index_files/code.png
Normal file
BIN
docs/source/_static/index_files/code.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
24
docs/source/_static/index_files/font-awesome.min.css
vendored
Normal file
24
docs/source/_static/index_files/font-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/source/_static/index_files/ico_mailme_01.png
Normal file
BIN
docs/source/_static/index_files/ico_mailme_01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
docs/source/_static/index_files/main.png
Normal file
BIN
docs/source/_static/index_files/main.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
1407
docs/source/_static/index_files/modernizr.js.download
Normal file
1407
docs/source/_static/index_files/modernizr.js.download
Normal file
File diff suppressed because it is too large
Load Diff
376
docs/source/_static/index_files/normalize.css
vendored
Normal file
376
docs/source/_static/index_files/normalize.css
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Corrects `block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects `inline-block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling for `hidden` attribute not present in IE 8/9.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* 1. Sets default font family to sans-serif.
|
||||
* 2. Prevents iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
|
||||
* Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Corrects font family set oddly in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets consistent quote types.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Removes border when inside `a` element in IE 8/9.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses margin not present in IE 8/9 and Safari 5.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects color not being inherited in IE 8/9.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects font family not being inherited in all browsers.
|
||||
* 2. Corrects font size not being inherited in all browsers.
|
||||
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Corrects inability to style clickable `input` types in iOS.
|
||||
* 3. Improves usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses box sizing set to `content-box` in IE 8/9.
|
||||
* 2. Removes excess padding in IE 8/9.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes inner padding and border in Firefox 4+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Removes default vertical scrollbar in IE 8/9.
|
||||
* 2. Improves readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
1
docs/source/_static/index_files/robot.svg
Normal file
1
docs/source/_static/index_files/robot.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1603718180952" class="icon" viewBox="0 0 1260 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4756" xmlns:xlink="http://www.w3.org/1999/xlink" width="246.09375" height="200"><defs><style type="text/css"></style></defs><path d="M1071.261538 992.492308H189.046154V622.276923c0-244.184615 196.923077-441.107692 441.107692-441.107692s441.107692 196.923077 441.107692 441.107692v370.215385z m-803.446153-78.769231h724.676923V622.276923c0-196.923077-165.415385-362.338462-362.338462-362.338461S267.815385 425.353846 267.815385 622.276923v291.446154z" p-id="4757" fill="#1296db"></path><path d="M193.929846 132.726154l65.614769-43.716923L408.024615 311.926154l-65.614769 43.638154z" p-id="4758" fill="#1296db"></path><path d="M220.553846 110.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4759" fill="#1296db"></path><path d="M855.118769 305.467077l148.558769-222.838154 65.536 43.716923-148.558769 222.916923z" p-id="4760" fill="#1296db"></path><path d="M1039.753846 110.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4761" fill="#1296db"></path><path d="M228.430769 882.215385H157.538462c-63.015385 0-118.153846-55.138462-118.153847-118.153847V630.153846c0-63.015385 55.138462-118.153846 118.153847-118.153846h70.892307v78.769231H157.538462c-23.630769 0-39.384615 15.753846-39.384616 39.384615v133.907692c0 23.630769 15.753846 39.384615 39.384616 39.384616h70.892307v78.769231zM1102.769231 882.215385h-70.892308v-78.769231h70.892308c23.630769 0 39.384615-15.753846 39.384615-39.384616V630.153846c0-23.630769-15.753846-39.384615-39.384615-39.384615h-70.892308v-78.769231h70.892308c63.015385 0 118.153846 55.138462 118.153846 118.153846v133.907692c0 63.015385-55.138462 118.153846-118.153846 118.153847z" p-id="4762" fill="#1296db"></path><path d="M448.984615 622.276923m-78.76923 0a78.769231 78.769231 0 1 0 157.538461 0 78.769231 78.769231 0 1 0-157.538461 0Z" p-id="4763" fill="#1296db"></path><path d="M811.323077 622.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4764" fill="#1296db"></path></svg>
|
After Width: | Height: | Size: 2.3 KiB |
289
docs/source/_templates/index.html
Normal file
289
docs/source/_templates/index.html
Normal file
@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- saved from url=(0029)http://matopen.com/dashboard/ -->
|
||||
<html lang="en" style="" class=" js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface no-generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
|
||||
<!-- Always force latest IE rendering engine or request Chrome Frame -->
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Use title if it's in the page YAML frontmatter -->
|
||||
<title>Welcome to PyMiner</title>
|
||||
|
||||
<meta name="description" content="XAMPP is an easy to install Apache distribution containing MariaDB, PHP and Perl.">
|
||||
<meta name="keywords" content="xampp, apache, php, perl, mariadb, open source distribution">
|
||||
|
||||
<link href="_static/index_files/normalize.css" rel="stylesheet" type="text/css"><link href="_static/index_files/all.css" rel="stylesheet" type="text/css">
|
||||
<link href="_static/index_files/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<script src="_static/index_files/all.js.download" async="" crossorigin="anonymous"></script><script id="facebook-jssdk" src="_static/index_files/all.js(1).download"></script><script src="_static/index_files/modernizr.js.download" type="text/javascript"></script>
|
||||
|
||||
|
||||
<link href="http://matopen.com/dashboard/images/favicon.png" rel="icon" type="image/png">
|
||||
|
||||
|
||||
<style type="text/css" data-fbcssmodules="css:fb.css.basecss:fb.css.dialog css:fb.css.iframewidget">.fb_hidden{position:absolute;top:-10000px;z-index:10001}.fb_reposition{overflow:hidden;position:relative}.fb_invisible{display:none}.fb_reset{background:none;border:0;border-spacing:0;color:#000;cursor:auto;direction:ltr;font-family:"lucida grande", tahoma, verdana, arial, sans-serif;font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:1;margin:0;overflow:visible;padding:0;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;visibility:visible;white-space:normal;word-spacing:normal}.fb_reset>div{overflow:hidden}@keyframes fb_transform{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.fb_animate{animation:fb_transform .3s forwards}
|
||||
.fb_dialog{background:rgba(82, 82, 82, .7);position:absolute;top:-10000px;z-index:10001}.fb_dialog_advanced{border-radius:8px;padding:10px}.fb_dialog_content{background:#fff;color:#373737}.fb_dialog_close_icon{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 0 transparent;cursor:pointer;display:block;height:15px;position:absolute;right:18px;top:17px;width:15px}.fb_dialog_mobile .fb_dialog_close_icon{left:5px;right:auto;top:5px}.fb_dialog_padding{background-color:transparent;position:absolute;width:1px;z-index:-1}.fb_dialog_close_icon:hover{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -15px transparent}.fb_dialog_close_icon:active{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -30px transparent}.fb_dialog_iframe{line-height:0}.fb_dialog_content .dialog_title{background:#6d84b4;border:1px solid #365899;color:#fff;font-size:14px;font-weight:bold;margin:0}.fb_dialog_content .dialog_title>span{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yd/r/Cou7n-nqK52.gif) no-repeat 5px 50%;float:left;padding:5px 0 7px 26px}body.fb_hidden{height:100%;left:0;margin:0;overflow:visible;position:absolute;top:-10000px;transform:none;width:100%}.fb_dialog.fb_dialog_mobile.loading{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/ya/r/3rhSv5V8j3o.gif) white no-repeat 50% 50%;min-height:100%;min-width:100%;overflow:hidden;position:absolute;top:0;z-index:10001}.fb_dialog.fb_dialog_mobile.loading.centered{background:none;height:auto;min-height:initial;min-width:initial;width:auto}.fb_dialog.fb_dialog_mobile.loading.centered #fb_dialog_loader_spinner{width:100%}.fb_dialog.fb_dialog_mobile.loading.centered .fb_dialog_content{background:none}.loading.centered #fb_dialog_loader_close{clear:both;color:#fff;display:block;font-size:18px;padding-top:20px}#fb-root #fb_dialog_ipad_overlay{background:rgba(0, 0, 0, .4);bottom:0;left:0;min-height:100%;position:absolute;right:0;top:0;width:100%;z-index:10000}#fb-root #fb_dialog_ipad_overlay.hidden{display:none}.fb_dialog.fb_dialog_mobile.loading iframe{visibility:hidden}.fb_dialog_mobile .fb_dialog_iframe{position:sticky;top:0}.fb_dialog_content .dialog_header{background:linear-gradient(from(#738aba), to(#2c4987));border-bottom:1px solid;border-color:#043b87;box-shadow:white 0 1px 1px -1px inset;color:#fff;font:bold 14px Helvetica, sans-serif;text-overflow:ellipsis;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0;vertical-align:middle;white-space:nowrap}.fb_dialog_content .dialog_header table{height:43px;width:100%}.fb_dialog_content .dialog_header td.header_left{font-size:12px;padding-left:5px;vertical-align:middle;width:60px}.fb_dialog_content .dialog_header td.header_right{font-size:12px;padding-right:5px;vertical-align:middle;width:60px}.fb_dialog_content .touchable_button{background:linear-gradient(from(#4267B2), to(#2a4887));background-clip:padding-box;border:1px solid #29487d;border-radius:3px;display:inline-block;line-height:18px;margin-top:3px;max-width:85px;padding:4px 12px;position:relative}.fb_dialog_content .dialog_header .touchable_button input{background:none;border:none;color:#fff;font:bold 12px Helvetica, sans-serif;margin:2px -12px;padding:2px 6px 3px 6px;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0}.fb_dialog_content .dialog_header .header_center{color:#fff;font-size:16px;font-weight:bold;line-height:18px;text-align:center;vertical-align:middle}.fb_dialog_content .dialog_content{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/y9/r/jKEcVPZFk-2.gif) no-repeat 50% 50%;border:1px solid #4a4a4a;border-bottom:0;border-top:0;height:150px}.fb_dialog_content .dialog_footer{background:#f5f6f7;border:1px solid #4a4a4a;border-top-color:#ccc;height:40px}#fb_dialog_loader_close{float:left}.fb_dialog.fb_dialog_mobile .fb_dialog_close_icon{visibility:hidden}#fb_dialog_loader_spinner{animation:rotateSpinner 1.2s linear infinite;background-color:transparent;background-image:url(https://static.xx.fbcdn.net/rsrc.php/v3/yD/r/t-wz8gw1xG1.png);background-position:50% 50%;background-repeat:no-repeat;height:24px;width:24px}@keyframes rotateSpinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
|
||||
.fb_iframe_widget{display:inline-block;position:relative}.fb_iframe_widget span{display:inline-block;position:relative;text-align:justify}.fb_iframe_widget iframe{position:absolute}.fb_iframe_widget_fluid_desktop,.fb_iframe_widget_fluid_desktop span,.fb_iframe_widget_fluid_desktop iframe{max-width:100%}.fb_iframe_widget_fluid_desktop iframe{min-width:220px;position:relative}.fb_iframe_widget_lift{z-index:1}.fb_iframe_widget_fluid{display:inline}.fb_iframe_widget_fluid span{width:100%}</style><style type="text/css">
|
||||
@font-face {
|
||||
font-family: "element-icons";
|
||||
src: url('chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff') format('woff'),
|
||||
url('chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
}
|
||||
</style></head>
|
||||
|
||||
<body class="index" cz-shortcut-listen="true">
|
||||
<div id="fb-root" class=" fb_reset"><div style="position: absolute; top: -10000px; width: 0px; height: 0px;"><div></div></div></div>
|
||||
<script>(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=277385395761685";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));</script>
|
||||
<div class="contain-to-grid">
|
||||
<nav class="top-bar" data-topbar="">
|
||||
<ul class="title-area">
|
||||
<li class="name">
|
||||
<h1><a href="http://matopen.com/dashboard/index.html">PyMiner</a></h1>
|
||||
</li>
|
||||
<li class="toggle-topbar menu-icon">
|
||||
<a href="http://matopen.com/dashboard/#">
|
||||
<span>Menu</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<section class="top-bar-section">
|
||||
<!-- Right Nav Section -->
|
||||
<ul class="right">
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B?sort_id=3137860">快速入门</a></li>
|
||||
<li class=""><a target="_blank" href="http://124.70.76.32:3000/PyMiner_dev_kit.zip">下载</a></li>
|
||||
<li class=""><a href="contents.html">开发者文档</a></li>
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/board">当前进度</a></li>
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/issues">问题汇总</a></li>
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E9%A1%B9%E7%9B%AE%E8%AF%B4%E6%98%8E?sort_id=2796707">项目说明</a></li>
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E5%A6%82%E4%BD%95%E5%8F%82%E4%B8%8E%E9%A1%B9%E7%9B%AE%EF%BC%9F?sort_id=2820268">如何参与</a></li>
|
||||
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E6%8D%90%E8%B5%A0?sort_id=2925146">捐赠</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="wrapper">
|
||||
<div class="hero">
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h1><img src="_static/index_files/robot.svg"> <span>开源的,跨平台数据处理解决方案。</span></h1>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h2>Welcome to PyMiner</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<p style="font-size: 20px;">PyMiner一款基于数据空间的数学工具,通过加载各种插件实现不同的需求,用易于操作的形式,在统一的界面中,通过数据计算实现数据科学家所设想的任务。</p>
|
||||
|
||||
<p style="font-size: 20px;color: #008CBA;">坚持对标国际一流,实现面向未来的设计,以自主创新为战略基石,积极吸收凝聚国内外各种优势资源和创新要素,致力于突破掌握一批数据算法模型以及仿真领域研制关键技术!</p>
|
||||
|
||||
<br>
|
||||
<div class="alert-box warning"><p style="font-size: 24px;">我们正在基于Python研究开源的 MATLAB,目前项目刚刚起步,需要更多热爱开源的Python、MATLAB等各路大牛加入,有兴趣请加入QQ群:945391275,欢迎大佬加入!</p><p></p></div>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
<ul id="downloads">
|
||||
<li id="download-windows">
|
||||
<h3><img src="_static/index_files/robot.svg">PyMiner <strong>源码仓库</strong> </h3>
|
||||
<table>
|
||||
<tbody><tr>
|
||||
<th width="25%">仓库名</th>
|
||||
<th width="25%" class="show-for-medium-up">版本号</th>
|
||||
<th width="25%" class="show-for-medium-up">源码地址</th>
|
||||
<th width="25%" class="show-for-medium-up">下载地址</th>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="1">Gitee</td>
|
||||
<td class="show-for-medium-up">PyMiner 1.0.1</td>
|
||||
<td class="show-for-medium-up">
|
||||
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://gitee.com/py2cn/pyminer">开源仓库 →</a>
|
||||
</td>
|
||||
<td class="show-for-medium-up">
|
||||
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="http://124.70.76.32:3000/PyMiner_dev_kit.zip">立即下载 →</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="1">Github</td>
|
||||
<td class="show-for-medium-up">PyMiner 1.0.1</td>
|
||||
<td class="show-for-medium-up">
|
||||
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://github.com/aboutlong/pyminer">开源仓库 →</a>
|
||||
</td>
|
||||
<td class="show-for-medium-up">
|
||||
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://gitee.com/py2cn/pyminer">立即下载 →</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</tbody></table>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
<div class="text-center">
|
||||
<a class="button icon" id="forum" target="_blank" href="https://www.kuxai.com/"> <img width="50" height="50" src="_static/index_files/robot.svg">Python机器学习社区
|
||||
<span>Aihub 一个朝气蓬勃的技术社区.</span>
|
||||
</a> </div>
|
||||
<p>
|
||||
|
||||
</p><h3><strong>技术说明</strong></h3>
|
||||
开发环境基于Python3.8+PyQt5.15,建议安装Pycharm创建python虚拟环境进行开发。
|
||||
<p></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3><strong>功能介绍</strong></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-8 columns">
|
||||
<ul>
|
||||
<li>强大的代码编辑器,让你误以为是专业的IDE,代码着色、智能提示应有尽有<br><img width="500" src="_static/index_files/code.png"></li>
|
||||
<li>开放的控制台,ipython集成,它能实现的我也可以</li>
|
||||
<li>强大的绘图功能,专业出版水准图片,增强型参数调整</li>
|
||||
<li>应用商店,开放的插件式开发,自定义专业领域的应用</li>
|
||||
<li>自建函数库、算法库,保持开放,面向未来!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="large-4 columns">
|
||||
<aside>
|
||||
<h4>特别感谢(捐赠人)</h4>
|
||||
<ul>
|
||||
<li> QuietPear</li>
|
||||
<li> 州的先生</li>
|
||||
<li> 邱峰</li>
|
||||
<li> 一个小白</li>
|
||||
|
||||
</ul>
|
||||
<h4>特别感谢(开发者)</h4>
|
||||
<ul>
|
||||
<li> 侯展意</li>
|
||||
<li> py2cn</li>
|
||||
<li> Junruoyu-Zheng</li>
|
||||
<li> nihk</li>
|
||||
<li> 心随风</li>
|
||||
<li> Irony</li>
|
||||
<li> 冰中火</li>
|
||||
<li> houxinluo</li>
|
||||
<li> cl-jiang</li>
|
||||
<li> 开始说故事</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3><strong>安装体验</strong></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<p>
|
||||
#第一步:下载源代码<br>
|
||||
git clone https://gitee.com/py2cn/pyminer.git <br>
|
||||
#第二步:同步安装依赖包和PyMiner,如果遇到安装失败的情况需要手动安装<br>
|
||||
python -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt <br>
|
||||
#第三步:运行主程序<br>
|
||||
python app2.py<br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3><strong>为什么要做这个?目的是什么?</strong></h3>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<p>
|
||||
2020年6月6日开始,哈工大等中国高校被禁用MATLAB。在此之前我们发现在工业软件、行业软件除了MATLAB以外,还有SAS、SPSS、FICO、Adobe等都是我们高度依赖且无法替代的行业软件。 为此,这个项目的根本目标就是实现MATLAB替代,短期内至少是实现在某一方面的替代,并逐步实现超越MATLAB,并以抛砖引玉的形式,让更多人加入到国产替代的行列中!PyMiner坚持对标国际一流,实现面向未来的设计,以自主创新为战略基石,积极吸收凝聚国内外各种优势资源和创新要素,致力于突破掌握一批数据算法模型以及仿真领域研制关键技术!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3><strong>预览截图</strong></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div style="text-align: center; margin-bottom: 1em;">
|
||||
<p style="text-align: left;">主界面截图</p>
|
||||
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/main.png" src="_static/index_files/main.png">
|
||||
</div>
|
||||
<div style="text-align: center; margin-bottom: 1em;">
|
||||
<p style="text-align: left;">绘图截图</p>
|
||||
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/check_data.png" src="_static/index_files/check_data.png">
|
||||
</div>
|
||||
<div style="text-align: center; margin-bottom: 1em;">
|
||||
<p style="text-align: left;">代码提示截图</p>
|
||||
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/code.png" src="_static/index_files/code.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="row">
|
||||
|
||||
<ul>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=sMTV0d3wwMmC096e09-d" style="text-decoration:none;"><img src="_static/index_files/ico_mailme_01.png"></a>
|
||||
</li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://mirrors.tuna.tsinghua.edu.cn/">清华大学开源软件镜像站</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.python.org/">Python官网</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.jetbrains.com/pycharm/download/">Pycharm</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.anaconda.com/">Anaconda</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://pandas.pydata.org/">Pandas</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://numpy.org/">Numpy</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://matplotlib.org/">matplotlib</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://scipy.org/">scipy</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://scikit-learn.org/stable/">scikit-learn</a></li>
|
||||
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://seaborn.pydata.org/">seaborn</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<p class="text-center">Copyright (c) 2020, PyMiner Development Team<br>
|
||||
<a target="_blank" href="https://beian.miit.gov.cn/">粤ICP备2020105986号</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- JS Libraries -->
|
||||
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
|
||||
<script src="_static/index_files/all.js(2).download" type="text/javascript"></script>
|
||||
|
||||
|
||||
</body><div style="all: initial;"><div></div></div></html>
|
@ -10,12 +10,17 @@
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
from recommonmark.transform import AutoStructify
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
base_path = path.abspath(path.join(__file__, '../../..'))
|
||||
sys.path.insert(0, base_path)
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))))
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'pyminer'
|
||||
copyright = '2020, py2cn'
|
||||
author = 'py2cn'
|
||||
@ -32,8 +37,10 @@ extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.mathjax',
|
||||
'numpydoc',
|
||||
'recommonmark',
|
||||
# 'numpydoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
@ -44,7 +51,7 @@ templates_path = ['_templates']
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'zh'
|
||||
language = 'zh_CN'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
@ -56,11 +63,47 @@ exclude_patterns = []
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
html_theme = "pydata_sphinx_theme"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# 将首页采用独立的模板进行渲染,即配置独立的首页,同时将 rst 自动生成的首页,由 index 调整为 contents 以免冲突
|
||||
html_additional_pages = {'index': 'index.html'}
|
||||
master_doc = 'contents'
|
||||
|
||||
add_module_names = False
|
||||
|
||||
# 配置TODO插件
|
||||
todo_include_todos = True
|
||||
|
||||
# 借助recommonmark插件,同时支持 *.md 和 *.rst 文件进行文档的撰写。
|
||||
source_suffix = {
|
||||
'.rst': 'restructuredtext',
|
||||
'.md': 'markdown',
|
||||
}
|
||||
|
||||
# 使得类的文档字符串和 ``__init__`` 方法的文档字符串都显出来
|
||||
autoclass_content = 'both'
|
||||
|
||||
# 使得生成的文档的顺序与源代码一致
|
||||
autodoc_member_order = 'bysource'
|
||||
|
||||
# 配置模板的主题样式
|
||||
html_style = 'css/pyminer.css'
|
||||
|
||||
# 显示 .. sectionauthor 指令指定的作者
|
||||
show_authors = True
|
||||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
}
|
||||
|
||||
|
||||
def setup(app: Sphinx):
|
||||
app.add_config_value('recommonmark_config', {
|
||||
'auto_toc_tree_section': 'Contents',
|
||||
}, True)
|
||||
app.add_transform(AutoStructify)
|
||||
|
20
docs/source/contents.rst
Normal file
20
docs/source/contents.rst
Normal file
@ -0,0 +1,20 @@
|
||||
===================================
|
||||
PyMiner帮助文档
|
||||
===================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
主程序部分 <pyminer/index.rst>
|
||||
界面部分 <pmg/index.rst>
|
||||
算法部分 <alg/index.rst>
|
||||
插件部分 <pkgs/index.rst>
|
||||
开发工具部分 <dev/index.rst>
|
||||
参与贡献 <contribute/index.rst>
|
||||
|
||||
模块索引
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
12
docs/source/contribute/index.md
Normal file
12
docs/source/contribute/index.md
Normal file
@ -0,0 +1,12 @@
|
||||
## 参与贡献
|
||||
|
||||
目前可以看我们的 [WIKI页面](https://gitee.com/py2cn/pyminer/wikis/%E4%B8%BB%E9%A1%B5?sort_id=2761029)。
|
||||
|
||||
参与贡献的第一步就是将WIKI上面的“参与贡献”转移到这里:P。
|
||||
|
||||
文档目前采用 Google 风格的文档字符串进行生成,所有内容都写在文档字符串里。
|
||||
包的文档字符串、模块的文档字符串、函数与方法的文档字符串,都可以被检索。
|
||||
各位开发者可以根据自己的需要在不同的位置添加文档字符串。
|
||||
|
||||
如果需要重新生成文档,可以删去`alg` 、`pkgs`、`pmg`、`pyminer`、`build`五个文件夹,
|
||||
然后运行`make_doc`即可重新生成。
|
@ -1,20 +0,0 @@
|
||||
.. pyminer documentation master file, created by
|
||||
.. sphinx-quickstart on Thu Oct 22 14:30:12 2020.
|
||||
.. You can adapt this file completely to your liking, but it should at least
|
||||
.. contain the root `toctree` directive.
|
||||
|
||||
Welcome to pyminer's documentation!
|
||||
===================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Readme
|
||||
|
||||
module <alg/modules.rst>
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
6
docs/templates/apidoc/module.rst_t
vendored
6
docs/templates/apidoc/module.rst_t
vendored
@ -1,6 +0,0 @@
|
||||
{% set names = basename.split('.') %}
|
||||
|
||||
{{ names[-1] | heading }}
|
||||
|
||||
.. automodule:: {{ qualname }}
|
||||
:members:
|
51
docs/templates/apidoc/package.rst_t
vendored
51
docs/templates/apidoc/package.rst_t
vendored
@ -1,51 +0,0 @@
|
||||
{%- macro automodule(modname, options) -%}
|
||||
.. automodule:: {{ modname }}
|
||||
:members: __doc__
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro toctree(docnames) -%}
|
||||
.. toctree::
|
||||
:maxdepth: {{ maxdepth }}
|
||||
{% for docname in docnames %}
|
||||
{{ docname }}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- if is_namespace %}
|
||||
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
|
||||
{% else %}
|
||||
{{- [pkgname, "package"] | join(" ") | e | heading }}
|
||||
{% endif %}
|
||||
|
||||
{%- if modulefirst and not is_namespace %}
|
||||
{{ automodule(pkgname, automodule_options) }}
|
||||
{% endif %}
|
||||
|
||||
{%- if subpackages %}
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
{{ toctree(subpackages) }}
|
||||
{% endif %}
|
||||
|
||||
{%- if submodules %}
|
||||
Submodules
|
||||
----------
|
||||
{% if separatemodules %}
|
||||
{{ toctree(submodules) }}
|
||||
{% else %}
|
||||
{%- for submodule in submodules %}
|
||||
{% if show_headings %}
|
||||
{{- [submodule, "module"] | join(" ") | e | heading(2) }}
|
||||
{% endif %}
|
||||
{{ automodule(submodule, automodule_options) }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if not modulefirst and not is_namespace %}
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
{{ automodule(pkgname, automodule_options) }}
|
||||
{% endif %}
|
10
docs/upload.py
Normal file
10
docs/upload.py
Normal file
@ -0,0 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 项目的根路径,即pyminer项目的根文件夹
|
||||
base = os.path.dirname(os.path.dirname(__file__))
|
||||
sys.path.insert(0, base)
|
||||
|
||||
html_path = os.path.join(base, 'docs', 'build', 'html')
|
||||
for line in os.popen(f'ghp-import -npfr git@gitee.com:py2cn/pyminer -b pages "{html_path}"'):
|
||||
print(line, end='')
|
42
examples/HereWeGo.py
Normal file
42
examples/HereWeGo.py
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
__author__ = 'boredbird'
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
import woe.feature_process as fp
|
||||
import woe.GridSearch as gs
|
||||
|
||||
if __name__ == '__main__':
|
||||
config_path = os.getcwd() + '\\config.csv'
|
||||
data_path = os.getcwd() + '\\UCI_Credit_Card.csv'
|
||||
feature_detail_path = os.getcwd() + '\\features_detail.csv'
|
||||
rst_pkl_path = os.getcwd() + '\\woe_rule.pkl'
|
||||
# train woe rule
|
||||
feature_detail, rst = fp.process_train_woe(infile_path=data_path
|
||||
, outfile_path=feature_detail_path
|
||||
, rst_path=rst_pkl_path
|
||||
, config_path=config_path)
|
||||
# proc woe transformation
|
||||
woe_train_path = os.getcwd() + '\\dataset_train_woed.csv'
|
||||
fp.process_woe_trans(data_path, rst_pkl_path, woe_train_path, config_path)
|
||||
# here i take the same dataset as test dataset
|
||||
woe_test_path = os.getcwd() + '\\dataset_test_woed.csv'
|
||||
fp.process_woe_trans(data_path, rst_pkl_path, woe_test_path, config_path)
|
||||
|
||||
print('###TRAIN SCORECARD MODEL###')
|
||||
params = {}
|
||||
params['dataset_path'] = woe_train_path
|
||||
params['validation_path'] = woe_test_path
|
||||
params['config_path'] = config_path
|
||||
|
||||
params['df_coef_path'] = os.getcwd() + '\\df_model_coef_path.csv'
|
||||
params['pic_coefpath'] = os.getcwd() + '\\model_coefpath.png'
|
||||
params['pic_performance'] = os.getcwd() + '\\model_performance_path.png'
|
||||
params['pic_coefpath_title'] = 'model_coefpath'
|
||||
params['pic_performance_title'] = 'model_performance_path'
|
||||
|
||||
params['var_list_specfied'] = []
|
||||
params['cs'] = np.logspace(-4, -1, 40)
|
||||
for key, value in params.items():
|
||||
print(key, ': ', value)
|
||||
gs.grid_search_lr_c_main(params)
|
43
examples/README.md
Normal file
43
examples/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Dataset Information
|
||||
|
||||
|
||||
This dataset contains information on default payments, demographic factors, credit data, history of payment, and bill statements of credit card clients in Taiwan from April 2005 to September 2005.
|
||||
|
||||
**YOU SHOULD SPECIFY THE VARIABLES DTYPES WITH config.csv**
|
||||
|
||||
Appointment:
|
||||
|
||||
continuous variables: is_tobe_bin=1 and is_candidate=1
|
||||
|
||||
discrete variables: is_tobe_bin=0 and is_candidate=1
|
||||
|
||||
## Content
|
||||
|
||||
|
||||
There are 25 variables:
|
||||
|
||||
- ID: ID of each client
|
||||
- LIMIT_BAL: Amount of given credit in NT dollars (includes individual and family/supplementary credit
|
||||
- SEX: Gender (1=male, 2=female)
|
||||
- EDUCATION: (1=graduate school, 2=university, 3=high school, 4=others, 5=unknown, 6=unknown)
|
||||
- MARRIAGE: Marital status (1=married, 2=single, 3=others)
|
||||
- AGE: Age in years
|
||||
- PAY_0: Repayment status in September, 2005 (-1=pay duly, 1=payment delay for one month, 2=payment delay for two months, ... 8=payment delay for eight months, 9=payment delay for nine months and above)
|
||||
- PAY_2: Repayment status in August, 2005 (scale same as above)
|
||||
- PAY_3: Repayment status in July, 2005 (scale same as above)
|
||||
- PAY_4: Repayment status in June, 2005 (scale same as above)
|
||||
- PAY_5: Repayment status in May, 2005 (scale same as above)
|
||||
- PAY_6: Repayment status in April, 2005 (scale same as above)
|
||||
- BILL_AMT1: Amount of bill statement in September, 2005 (NT dollar)
|
||||
- BILL_AMT2: Amount of bill statement in August, 2005 (NT dollar)
|
||||
- BILL_AMT3: Amount of bill statement in July, 2005 (NT dollar)
|
||||
- BILL_AMT4: Amount of bill statement in June, 2005 (NT dollar)
|
||||
- BILL_AMT5: Amount of bill statement in May, 2005 (NT dollar)
|
||||
- BILL_AMT6: Amount of bill statement in April, 2005 (NT dollar)
|
||||
- PAY_AMT1: Amount of previous payment in September, 2005 (NT dollar)
|
||||
- PAY_AMT2: Amount of previous payment in August, 2005 (NT dollar)
|
||||
- PAY_AMT3: Amount of previous payment in July, 2005 (NT dollar)
|
||||
- PAY_AMT4: Amount of previous payment in June, 2005 (NT dollar)
|
||||
- PAY_AMT5: Amount of previous payment in May, 2005 (NT dollar)
|
||||
- PAY_AMT6: Amount of previous payment in April, 2005 (NT dollar)
|
||||
- default.payment.next.month: Default payment (1=yes, 0=no)
|
43
examples/README_ZH.md
Normal file
43
examples/README_ZH.md
Normal file
@ -0,0 +1,43 @@
|
||||
# 数据集信息
|
||||
|
||||
|
||||
本数据集包含2005年4月至2005年9月台湾信用卡客户的逾期、个人信息、信用数据、付款历史及账单信息。
|
||||
|
||||
**你应该通过 config.csv 指定变量类型**
|
||||
|
||||
约定:
|
||||
|
||||
连续变量: is_tobe_bin=1 且 is_candidate=1
|
||||
|
||||
离散变量: is_tobe_bin=0 且 is_candidate=1
|
||||
|
||||
## 内容
|
||||
|
||||
|
||||
本数据集包含25个变量:
|
||||
|
||||
- ID: 每个客户的ID
|
||||
- LIMIT_BAL:授信金额(新台币,含个人及家庭/补充授信)
|
||||
- SEX: 性别 (1=男, 2=女)
|
||||
- EDUCATION: (1=研究生, 2=本科, 3=高中, 4=其他, 5=未知, 6=未知)
|
||||
- MARRIAGE: 婚姻状态 (1=已婚, 2=单身, 3=其他)
|
||||
- AGE: 年龄
|
||||
- PAY_0: 2005年9月还款状态 (-1=正常支付, 1=付款延迟1个月, 2=付款延迟2个月, ... 8=付款延迟8个月, 9=付款延迟9个月及以上)
|
||||
- PAY_2: 2005年8月还款状态 (单位同上)
|
||||
- PAY_3: 2005年7月还款状态 (单位同上)
|
||||
- PAY_4: 2005年6月还款状态 (单位同上)
|
||||
- PAY_5: 2005年5月还款状态 (单位同上)
|
||||
- PAY_6: 2005年4月还款状态 (单位同上)
|
||||
- BILL_AMT1: 2005年9月的账单金额 (新台币)
|
||||
- BILL_AMT2: 2005年8月的账单金额 (新台币)
|
||||
- BILL_AMT3: 2005年7月的账单金额 (新台币)
|
||||
- BILL_AMT4: 2005年6月的账单金额 (新台币)
|
||||
- BILL_AMT5: 2005年5月的账单金额 (新台币)
|
||||
- BILL_AMT6: 2005年4月的账单金额 (新台币)
|
||||
- PAY_AMT1: 2005年9月的提前还款金额 (新台币)
|
||||
- PAY_AMT2: 2005年8月的提前还款金额 (新台币)
|
||||
- PAY_AMT3: 2005年7月的提前还款金额 (新台币)
|
||||
- PAY_AMT4: 2005年6月的提前还款金额 (新台币)
|
||||
- PAY_AMT5: 2005年5月的提前还款金额 (新台币)
|
||||
- PAY_AMT6: 2005年4月的提前还款金额 (新台币)
|
||||
- target: 是否逾期 (1=是, 0=否)
|
Can't render this file because it is too large.
|
Can't render this file because it is too large.
|
Can't render this file because it is too large.
|
Can't render this file because it is too large.
|
BIN
examples/woe_rule.pkl
Normal file
BIN
examples/woe_rule.pkl
Normal file
Binary file not shown.
7
pip.ini
Normal file
7
pip.ini
Normal file
@ -0,0 +1,7 @@
|
||||
[global]
|
||||
|
||||
index-url=http://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
[install]
|
||||
|
||||
trusted-host=mirrors.aliyun.com
|
@ -1,254 +0,0 @@
|
||||
[TOC]
|
||||
|
||||
# pmgwidgets常用控件介绍
|
||||
|
||||
``` python
|
||||
from pmgwidgets import {控件名}
|
||||
```
|
||||
用以上语句即可导入相应的控件。
|
||||
控件的有关示例见pmgwidgets/tests文件夹。
|
||||
## 设置控件SettingsPanel
|
||||
查看本文中的示例即可。运行示例即可得到以下界面:
|
||||
|
||||
![](doc_figures/settings_panel.png)
|
||||
|
||||
### 数据结构与格式
|
||||
|
||||
创建这个界面只需要一个json式的数据结构,如下所示:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150)),
|
||||
('number', 'height', 'How High could This Plane fly?', 12000, 'm', (10, 20000)),
|
||||
('bool', 'sport', 'do you like sport', True),
|
||||
('choose_box', 'plane_type', 'plane type', 'f22', ['f22', 'f18', 'j20', 'su57'],
|
||||
['f22战斗机', 'f18战斗轰炸机', 'j20战斗机', 'su57战斗机']),
|
||||
('color', 'color', 'Which color do u like?', (0, 200, 0))]
|
||||
```
|
||||
这些数据的格式为:
|
||||
数据类型;数据名称;提示信息;初始值。第四位之后的其他数据为修饰信息,比如单位、范围等。
|
||||
|
||||
| 返回值类型 | 1:选择器名称 | 2:数据名称 | 3:提示信息 | 4:初始值 | 5 | 6 |
|
||||
| ---------------------------------------- | ------------ | ---------- | ----------- | -------------------------- | -------- | --------------- |
|
||||
| 字符串型(str) | 'line_edit' | str | str | 初始值:str | / | / |
|
||||
| 整型或者浮点(int/float)字符串型(str) | 'number' | 名称:str | str | int/float初始值:str | 单位str | 范围(min,max) |
|
||||
| 布尔型(bool) | 'bool' | str | str | bool | / | / |
|
||||
| 任意类型,多选一(str) | 'choose_box' | str | str | object(任意类型)* | 选项列表 | 选项文本列表 |
|
||||
| 颜色(返回形如'#a0b89d'的颜色字符串) | 'color' | 'color' | str | tupleRGB,每位为0~255的整数 | / | |
|
||||
*:注意,任意类型,多选一的下拉列表中,列表可以填入任意类型。但是你所输入的初始值,必须在选项列表中存在,否则会抛出异常。
|
||||
|
||||
### 创建一个设置面板:
|
||||
|
||||
- 控件列表可以在初始化的时候传入,像这样:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
```
|
||||
|
||||
- 控件列表也可以在初始化完成之后传入:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel()
|
||||
sp.set_items(views)
|
||||
```
|
||||
|
||||
写法与初始化时相同。
|
||||
|
||||
- `set_items`可以多次调用。如果调用的时候设置面板不为空,那么之前的设置项都会被清空。
|
||||
|
||||
### 获取其上的控件
|
||||
|
||||
调用`get_ctrl(ctrl_name:str)`方法,可以获取其上的控件。
|
||||
|
||||
如:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
name_widget = sp.get_ctrl('name')
|
||||
```
|
||||
|
||||
- 注意:如果控件名称不存在,则返回None。注意做好类型判断。
|
||||
|
||||
### 获取值
|
||||
|
||||
#### 获取单个控件值
|
||||
|
||||
获取控件之后,调用控件的set_value方法。
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
name_widget = sp.get_ctrl('name')
|
||||
name_widget.get_value()
|
||||
```
|
||||
|
||||
|
||||
#### 获取整体值
|
||||
|
||||
调用SettingsPanel的get_value()方法,可以返回一个字典。键就是以上表格的“数据名称”列,返回的是相应的值。
|
||||
|
||||
如果未做任何改动,那么返回的值就是初始值,也就是表格“初始值”列的内容。
|
||||
|
||||
比如之前所述的views列表输入的初始值,最终得出的结果是这样的dict:
|
||||
|
||||
```python
|
||||
values = sp.get_value()
|
||||
values = {'name': 'hzy', 'age': 88.0, 'height': 12000.0, 'sport': True, 'plane_type': 'f22', 'color': (0, 200, 0)}
|
||||
```
|
||||
|
||||
### 设置值
|
||||
|
||||
比如之前所述的views列表输入的初始值,最终得出的结果是这样的dict:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'James'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
name_widget = sp.get_ctrl('name')
|
||||
name_widget.set_value("John")
|
||||
```
|
||||
|
||||
set_value方法传入的参数,类型必须与初始值相同。
|
||||
|
||||
### 设置控件的参数
|
||||
|
||||
调用`set_params(*params)`方法。
|
||||
|
||||
params指的是从表格的‘初始值’列之后的几项。比如选择菜单的选项、数据型输入框的范围。
|
||||
|
||||
控件的参数指的就是从第5项开始(含第5项)以后的内容。比如对于一个数值型控件:
|
||||
|
||||
如:
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
num_widget = sp.get_ctrl('name')
|
||||
if num_widget is not None:
|
||||
num_widget.set_params('years old',(10,145))
|
||||
|
||||
```
|
||||
|
||||
以上示例就是把年龄范围从0~150岁设置到了10~145岁。如果你不希望改变其他设置(比如这里的‘years old’),将原有的值重新写一遍就可以了。
|
||||
|
||||
- 注意:如果控件名称不存在,则返回None。注意做好类型判断。
|
||||
|
||||
### 禁用和启用单个控件(设置灰色)
|
||||
|
||||
```python
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
sp.get_ctrl('name').setEnabled(False) # 禁用name控件
|
||||
sp.get_ctrl('name').setEnabled(True) # 启用name控件
|
||||
```
|
||||
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from pmgwidgets import SettingsPanel
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
# 类型;名称;显示的提示文字;初始值;//单位;范围
|
||||
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
|
||||
('number', 'age', 'How old are you?', 88, 'years old', (0, 150)),
|
||||
('number', 'height', 'How High could This Plane fly?', 12000, 'm', (10, 20000)),
|
||||
('bool', 'sport', 'do you like sport', True),
|
||||
('choose_box', 'plane_type', 'plane type', 'f22', ['f22', 'f18', 'j20', 'su57'],
|
||||
['f22战斗机', 'f18战斗轰炸机', 'j20战斗机', 'su57战斗机']),
|
||||
('color', 'color', 'Which color do u like?', (0, 200, 0))]
|
||||
sp = SettingsPanel(views=views, layout_dir='v')
|
||||
sp.widgets_dic['plane_type'].set_choices(['aaa', 'vvvvv', 'xxxxxx'])
|
||||
sp.set_items(views[3:6])
|
||||
sp.show()
|
||||
sp2 = SettingsPanel(views=views, layout_dir='h')
|
||||
sp2.show()
|
||||
sp2.setMaximumHeight(30)
|
||||
val = sp.get_value() # 返回一个字典。初始值为表格的第二列:第四列。
|
||||
print(val)
|
||||
sys.exit(app.exec_())
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 文件树控件
|
||||
class PMGFilesTreeview(QTreeView):
|
||||
### 如何插入界面
|
||||
def __init__(self, initial_dir: str = '', parent=None):
|
||||
initial_dir:str,初始时的路径。
|
||||
parent:父控件,可以为None。
|
||||
### 信号:
|
||||
- new_file_signal = pyqtSignal(str)
|
||||
新建文件信号,返回一个参数,是新建文件的绝对路径
|
||||
- new_folder_signal = pyqtSignal(str)
|
||||
新建文件夹信号,返回一个参数,是新建文件夹的绝对路径
|
||||
- delete_file_signal = pyqtSignal(str)
|
||||
删除文件或者文件夹信号,返回一个参数,是文件夹的绝对路径。
|
||||
- rename_file_signal = pyqtSignal(str, str)
|
||||
文件重命名的信号,返回两个参数,分别是重命名之前的绝对路径和重命名之后的绝对路径。
|
||||
|
||||
注意:以上信号都是只有操作成功才会被触发的。**如果操作不成功(比如重命名时存在相同文件、删除文件时权限不够),那么就不会触发。**。
|
||||
|
||||
## 容器控件
|
||||
|
||||
### 流式布局控件PMFlowArea
|
||||
|
||||
流式布局控件为PMFlowArea,示例见tests文件夹的flow_layout_widget.py。
|
||||
|
||||
运行这个例子可以发现以下效果:
|
||||
![](doc_figures/pmflowarea_1.png)
|
||||
![](doc_figures/pmflowarea_2.png)
|
||||
可以看到,布局在界面左右拖拽的时候,按钮会自动重排。
|
||||
问题:控件库的按钮自动重排之前,似乎不是从0,0开始添加按钮的。
|
||||
### 选项卡控件PMGTabWidget
|
||||
(这里名字不对!需要改过来!!)
|
||||
是选项卡控件。
|
||||
选项卡控件的特点是,它的setup_ui方法中,会调用子界面的setup_ui方法。同理也适用于bind_events。
|
||||
### 可停靠控件PMGDockWidget
|
||||
额外定义了raise_into_view的方法,调用这方法时可以保证此控件提升到窗口最顶端。
|
||||
[TODO]:需要考虑将窗口也增设进来!
|
||||
|
||||
## 相关函数和方法
|
||||
### 文件操作
|
||||
#### rename_file(prev_absolute_path:str, new_absolute_path:str)->bool
|
||||
重命名文件或者文件夹
|
||||
prev_absolute_path:之前的绝对路径
|
||||
new_absolute_path:新的绝对路径
|
||||
返回值:True为操作成功,False为不成功(比如已有文件或者文件夹与新的名称重名)
|
||||
#### move_to_trash(path:str)->bool
|
||||
path:要移到回收站的文件夹的绝对路径。
|
||||
返回值:True为操作成功,False为不成功。
|
||||
### 执行系统命令
|
||||
[!TODO]
|
||||
#### run_command_in_terminal(打开系统终端并在其中执行命令。)
|
||||
在终端命令行中运行命令。
|
||||
```
|
||||
from pmgwidgets import run_command_in_terminal
|
||||
|
||||
|
||||
def test_run_in_terminal():
|
||||
import time
|
||||
run_command_in_terminal('dir', close_mode='no')
|
||||
time.sleep(1)
|
||||
run_command_in_terminal('dir', close_mode='wait_key')
|
||||
time.sleep(1)
|
||||
run_command_in_terminal('dir', close_mode='auto')
|
||||
|
||||
|
||||
test_run_in_terminal()
|
||||
```
|
||||
close_mode的意思时命令执行完之后终端怎么做。当其为‘no'的时候,终端不退出,可以输入命令继续执行下一条;显示为'wait_key'的时候,终端等待按任意键退出;显示为‘auto’的时候,终端执行完之后就退出——所以执行dir一类秒完成的命令,就会闪现一下,然后便不见了。
|
||||
|
||||
# pmgwindows常用控件介绍
|
@ -1,12 +0,0 @@
|
||||
from .table import PMGTableWidget, PMTableView, PMGTableTabWidget, PMGTableViewer
|
||||
from .containers import PMFlowArea, PMScrollArea, PMTabWidget, PMFlowLayout, PMFlowLayoutWithGrid, \
|
||||
PMGDockWidget
|
||||
from .basicwidgets import PMDockObject, PMPushButtonPane, PMToolButton
|
||||
from .toolbars import PMGToolBar, TopToolBar, ActionWithMessage
|
||||
from .sourcemgr import create_icon
|
||||
from .normal import SettingsPanel, center_window, set_always_on_top, set_closable, set_minimizable
|
||||
from .views import PMGFilesTreeview, PMFileSystemModel, PMJsonTreeViewer
|
||||
from .display import *
|
||||
from .platform import *
|
||||
from .communication import *
|
||||
from .communication.test.clientgeneral import GeneralClient
|
@ -1,3 +0,0 @@
|
||||
from .pmpushbuttons import PMPushButtonPane
|
||||
from .toolbutton import PMToolButton
|
||||
from .object import PMDockObject
|
@ -1,34 +0,0 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QScrollArea
|
||||
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
|
||||
class PMScrollableLabel(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QVBoxLayout()
|
||||
self.scroll_area = QScrollArea()
|
||||
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
self.label = QLabel()
|
||||
layout_label = QVBoxLayout()
|
||||
layout_label.addWidget(self.label)
|
||||
self.scroll_area.setLayout(layout_label)
|
||||
|
||||
layout.addWidget(self.scroll_area)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
self.setText = self.label.setText
|
||||
self.setWordWrap = self.label.setWordWrap
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import sys
|
||||
app = QApplication(sys.argv)
|
||||
w = PMScrollableLabel()
|
||||
w.setWordWrap(True)
|
||||
w.setText('aaaaaaaaa ' * 10000)
|
||||
w.show()
|
||||
sys.exit(app.exec_())
|
@ -1,43 +0,0 @@
|
||||
from typing import Tuple
|
||||
|
||||
from PyQt5.QtWidgets import QWidget
|
||||
|
||||
|
||||
class PMDockObject(object):
|
||||
"""
|
||||
修改:
|
||||
原先,主窗口中的各个可停靠窗口,在点击右上角关闭按钮的时候会隐藏,可以在视图菜单中打开。
|
||||
但是当控件中有on_closed_action属性,且值为‘delete’的时候,控件就会被回收。
|
||||
为了实现控件的管理,控件需要继承PMDockObject,并且需要用多继承的方式。
|
||||
|
||||
from pyminer2.ui.generalwidgets import PMDockObject
|
||||
这个PMDockObject中定义了一些方法,作为补充。
|
||||
|
||||
class PMDockObject(object):
|
||||
on_closed_action = 'hide' # 或者'delete'。
|
||||
|
||||
def raise_widget_to_visible(self, widget: 'QWidget'):
|
||||
pass
|
||||
|
||||
def on_dock_widget_deleted(self):
|
||||
pass
|
||||
|
||||
"""
|
||||
on_closed_action = 'hide' # 或者'delete'。
|
||||
|
||||
def is_temporary(self)->bool:
|
||||
"""
|
||||
如果为True,相应的控件将在窗口关闭时删除,并且不会记忆其位置。如果为False,则相应的控件不会被删除,且其位置将被记忆。
|
||||
|
||||
默认返回False。
|
||||
"""
|
||||
return False
|
||||
|
||||
def raise_widget_to_visible(self, widget: 'QWidget'):
|
||||
pass
|
||||
|
||||
def on_dock_widget_deleted(self):
|
||||
pass
|
||||
|
||||
def get_split_portion_hint(self) -> Tuple[int, int]:
|
||||
return (None, None)
|
@ -1,63 +0,0 @@
|
||||
"""
|
||||
pmpushbuttons是一个按钮组,专门负责向工具栏中插入按钮。
|
||||
其中PMPushButtonPane是按钮的载体,可以插入竖向排布的两个到三个按钮。
|
||||
作者:Zhanyi Hou
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QMenu, QLabel
|
||||
|
||||
from pmgwidgets.sourcemgr import create_icon
|
||||
|
||||
|
||||
class PMPushButtonPane(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def add_height_occu_buttons(self):
|
||||
button_num = 3
|
||||
btn_list = []
|
||||
for i in range(button_num):
|
||||
btn = QLabel()
|
||||
btn.setText(' ')
|
||||
btn.setProperty('qssp', 'occu')
|
||||
btn_list.append(btn)
|
||||
btn.setObjectName('space_occupation_button')
|
||||
self.layout.addWidget(btn)
|
||||
return btn_list
|
||||
|
||||
def add_buttons(self, button_num: int = 2, text: list = None, icon_path: list = None,
|
||||
menu: list = None) -> List[QPushButton]:
|
||||
if text is None:
|
||||
text = [''] * button_num
|
||||
if icon_path is None:
|
||||
icon_path = [None] * button_num
|
||||
if menu is None:
|
||||
menu = [None] * button_num
|
||||
if len(text) != button_num or len(
|
||||
icon_path) != button_num or len(menu) != button_num:
|
||||
raise Exception('text,icon和menu参数都必须为长为2的可迭代对象。')
|
||||
qssproperty = "minibutton3"
|
||||
if button_num == 2:
|
||||
qssproperty = "minibutton2"
|
||||
|
||||
btn_list = []
|
||||
for i in range(button_num):
|
||||
btn = self.add_button(text=text[i], icon=create_icon(icon_path[i]), menu=menu[i], qssproperty=qssproperty)
|
||||
btn_list.append(btn)
|
||||
return btn_list
|
||||
|
||||
def add_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None, qssproperty = "minibutton3") \
|
||||
-> QPushButton:
|
||||
pbtn = QPushButton()
|
||||
pbtn.setText(text)
|
||||
if icon is not None:
|
||||
pbtn.setIcon(icon)
|
||||
if menu is not None:
|
||||
pbtn.setMenu(menu)
|
||||
pbtn.setProperty("qssp", qssproperty)
|
||||
self.layout.addWidget(pbtn)
|
||||
return pbtn
|
@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding:utf-8 -*-
|
||||
from PyQt5.QtCore import QEvent, QTimer
|
||||
from PyQt5.QtGui import QColor
|
||||
from PyQt5.QtWidgets import QToolButton, QGraphicsDropShadowEffect, QMenu, QWidget, QHBoxLayout
|
||||
|
||||
|
||||
class PMMenu(QMenu):
|
||||
def __init__(self, tool_button: 'PMToolButton' = None):
|
||||
super().__init__()
|
||||
self.tool_button = tool_button
|
||||
|
||||
def leaveEvent(self, a0: QEvent) -> None:
|
||||
print('leave menu', self.underMouse(), self.tool_button.underMouse())
|
||||
# timer = QTimer()
|
||||
QTimer.singleShot(50, self.tool_button.hide_menu)
|
||||
# if self.tool_button.underMouse():
|
||||
# return
|
||||
# else:
|
||||
# self.hide()
|
||||
|
||||
|
||||
class PMToolButton(QToolButton):
|
||||
shadow = QGraphicsDropShadowEffect() # 实例阴影
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.shadow.setColor(QColor(63, 72, 204)) # 设置阴影颜色
|
||||
self.shadow.setOffset(0, 0) # 设置阴影方向
|
||||
self.setMinimumWidth(60)
|
||||
self.setMinimumHeight(40)
|
||||
self.clicked.connect(self.set_btn_clicked_effect)
|
||||
self.menu = PMMenu(tool_button=self)
|
||||
self.setMenu(self.menu)
|
||||
self.menu.addAction('新建行')
|
||||
self.menu.addAction('新建列')
|
||||
self.menu.addAction('删除行')
|
||||
self.menu.addAction('删除列')
|
||||
# self.setFocusPolicy()
|
||||
|
||||
# self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
# self.setPopupMode(QToolButton.InstantPopup)
|
||||
|
||||
def set_btn_clicked_effect(self):
|
||||
# 设置模糊度并为按钮添加阴影
|
||||
self.shadow.setBlurRadius(20)
|
||||
self.setGraphicsEffect(self.shadow)
|
||||
|
||||
def unset_btn_clicked_effect(self):
|
||||
# 设置模糊度为0 间接取消阴影
|
||||
self.shadow.setBlurRadius(0)
|
||||
|
||||
def show_menu(self):
|
||||
if self.underMouse() or self.menu.underMouse():
|
||||
self.menu.popup(self.mapToGlobal(self.pos()))
|
||||
self.grabMouse()
|
||||
|
||||
def hide_menu(self):
|
||||
if not (self.underMouse() or self.menu.underMouse()):
|
||||
self.menu.hide()
|
||||
# self.clearFocus()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import sys
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
w = QWidget()
|
||||
layout = QHBoxLayout()
|
||||
w.setLayout(layout)
|
||||
for i in range(3):
|
||||
w1 = PMToolButton()
|
||||
layout.addWidget(w1)
|
||||
w1.setText('aaa')
|
||||
w.show()
|
||||
sys.exit(app.exec_())
|
@ -1 +0,0 @@
|
||||
from pmgwidgets.communication.test import *
|
@ -1,22 +0,0 @@
|
||||
if __name__=='__main__':
|
||||
from pmgwidgets import BaseClient
|
||||
|
||||
import numpy as np
|
||||
|
||||
x = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
y = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
print(x)
|
||||
c = BaseClient()
|
||||
|
||||
print('set_var')
|
||||
c.set_var('x', x)
|
||||
c.set_var('y', y)
|
||||
c.set_var('z', y)
|
||||
c.set_var('w', y)
|
||||
c.set_var('t', y)
|
||||
c.set_var('y', y)
|
||||
c.get_var('x')
|
||||
print(c.get_all_vars())
|
||||
print(c.get_all_var_names())
|
||||
# while (1):
|
||||
# time.sleep(1)
|
@ -1,5 +0,0 @@
|
||||
from pmgwidgets import GeneralClient
|
||||
import time
|
||||
__client = GeneralClient()
|
||||
while(1):
|
||||
time.sleep(1)
|
@ -1 +0,0 @@
|
||||
from pmgwidgets.communication.test.baseclient import BaseClient, get_style_sheet
|
@ -1,222 +0,0 @@
|
||||
import socket
|
||||
import cloudpickle
|
||||
import base64
|
||||
import sys
|
||||
import json
|
||||
|
||||
|
||||
def get_style_sheet() -> str:
|
||||
return BaseClient().get_style_sheet()
|
||||
|
||||
|
||||
class BaseClient():
|
||||
def init_socket(self, port: int = 12306):
|
||||
host = 'localhost'
|
||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 在客户端开启心跳维护
|
||||
client.connect((host, port))
|
||||
return client
|
||||
|
||||
def get_var(self, data_name: str):
|
||||
"""
|
||||
从工作空间获取数据
|
||||
:param data_name:
|
||||
:return:
|
||||
"""
|
||||
payload = {
|
||||
"method": "read_p",
|
||||
"params": [data_name],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
b = self.request_data(json.dumps(payload).encode('utf-8'))
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
data_b64 = response_dic.get(data_name)
|
||||
|
||||
if data_b64 is not None:
|
||||
s = self.load_data_from_b64(data_b64)
|
||||
return s
|
||||
return None
|
||||
|
||||
def dump_data_from_b64(self, data: object) -> str:
|
||||
try:
|
||||
data_dumped_bytes = cloudpickle.dumps(data)
|
||||
return base64.b64encode(data_dumped_bytes).decode('ascii')
|
||||
except:
|
||||
pass
|
||||
|
||||
def load_data_from_b64(self, data_b64: str) -> object:
|
||||
try:
|
||||
return cloudpickle.loads(base64.b64decode(data_b64))
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def delete_var(self, var_name: list, provider: str):
|
||||
payload = {
|
||||
"method": "delete_variable",
|
||||
"params": [var_name, provider],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
# print('set_data', 'set_data')
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
# print('get_all_data', b)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
if response_dic['message'] == 'succeed':
|
||||
return response_dic['var_names']
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_all_public_var_names(self):
|
||||
payload = {
|
||||
"method": "get_all_public_variable_names",
|
||||
"params": [],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
if response_dic['message'] == 'succeed':
|
||||
return response_dic['var_names']
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_all_var_names(self):
|
||||
payload = {
|
||||
"method": "get_all_variable_names",
|
||||
"params": [],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
if response_dic['message'] == 'succeed':
|
||||
return response_dic['var_names']
|
||||
else:
|
||||
return []
|
||||
|
||||
def set_var_dic(self, var_dic: dict, provider: str = 'server'):
|
||||
# print(var_dic)
|
||||
dic = {}
|
||||
for k in var_dic.keys():
|
||||
try:
|
||||
b64 = self.dump_data_from_b64(var_dic[k])
|
||||
dic[k] = b64
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
dumped_data = self.dump_data_from_b64(dic)
|
||||
payload = {
|
||||
"method": "write_var_dic",
|
||||
"params": [dumped_data, provider],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
return response_dic
|
||||
|
||||
def get_all_vars(self):
|
||||
payload = {
|
||||
"method": "get_var_dic",
|
||||
"params": [],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
if response_dic.get('var_dic') != None:
|
||||
b = response_dic.get('var_dic')
|
||||
return self.load_data_from_b64(b)
|
||||
else:
|
||||
return response_dic
|
||||
|
||||
def set_var(self, data_name: str, data: object, provider: str = 'server'):
|
||||
"""
|
||||
向数据管理类中写入数据
|
||||
:param data_name:
|
||||
:param data:
|
||||
:param provider:
|
||||
:return:
|
||||
"""
|
||||
if sys.getsizeof(data) / (1024 * 1024) > 150:
|
||||
raise MemoryError('Data \'%s\' size %f MB is larger than limit ( 150MB ) .' % (
|
||||
data_name, sys.getsizeof(data) / 1024 / 1024))
|
||||
dumped_data = self.dump_data_from_b64(data)
|
||||
message = 'Data size after b64 encode %f MB is too large,it should less than 300MB after base64 encoded.\n ' % (
|
||||
sys.getsizeof(dumped_data) / 1024 / 1024)
|
||||
|
||||
assert sys.getsizeof(
|
||||
dumped_data) / 1024 / 1024 <= 300, MemoryError(message)
|
||||
payload = {
|
||||
"method": "write_p",
|
||||
"params": [data_name, dumped_data, provider],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
payload_byte = json.dumps(payload).encode('utf-8')
|
||||
b = self.request_data(payload_byte)
|
||||
response_dic: dict = json.loads(b.decode('utf-8'))
|
||||
|
||||
return response_dic
|
||||
|
||||
def get_settings(self) -> dict:
|
||||
"""
|
||||
获取主界面的设置项。
|
||||
:return:
|
||||
"""
|
||||
payload = {
|
||||
"method": "get_settings",
|
||||
"params": [],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
b = self.request_data(json.dumps(payload).encode('utf-8'))
|
||||
result_dic: dict = json.loads(b.decode('utf-8')).get('settings')
|
||||
assert result_dic is not None
|
||||
return result_dic
|
||||
|
||||
def get_style_sheet(self) -> str:
|
||||
"""
|
||||
获取主界面的样式表
|
||||
:return:
|
||||
"""
|
||||
payload = {
|
||||
"method": "get_style_sheet",
|
||||
"params": [],
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
}
|
||||
b = self.request_data(json.dumps(payload).encode('utf-8'))
|
||||
result = json.loads(b.decode('utf-8')).get('style_sheet')
|
||||
assert result is not None
|
||||
return result
|
||||
|
||||
def request_data(self, byte_data) -> bytes:
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 12306
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义socket类型,网络通信,TCP
|
||||
s.connect((HOST, PORT)) # 要连接的IP与端口
|
||||
s.send(b'request_data')
|
||||
msg = s.recv(1024)
|
||||
s.sendall(byte_data + b'PMEND') # 把命令发送给对端
|
||||
l = []
|
||||
while (1):
|
||||
b = s.recv(65536)
|
||||
l.append(b)
|
||||
if b.endswith(b'PMEND'):
|
||||
# p = p.strip(b'PMEND')
|
||||
l[-1] = l[-1].strip(b'PMEND')
|
||||
break
|
||||
data = b''.join(l) # 把接收的数据定义为变量
|
||||
|
||||
s.close() # 关闭连接
|
||||
return data
|
@ -1,122 +0,0 @@
|
||||
# coding=utf-8
|
||||
__author__ = '药师Aric'
|
||||
|
||||
import base64
|
||||
import json
|
||||
import zlib
|
||||
from typing import List
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
import cloudpickle
|
||||
|
||||
'''
|
||||
client端
|
||||
长连接,短连接,心跳
|
||||
'''
|
||||
import socket
|
||||
import time
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtCore import QObject, QThread, pyqtSignal, QTimer
|
||||
import sys
|
||||
|
||||
from pmgwidgets.communication.test.util import timeit, receive
|
||||
from pmgwidgets.communication.test.baseclient import BaseClient
|
||||
|
||||
|
||||
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
|
||||
return packet_bytes.split(b'PMEND')
|
||||
|
||||
|
||||
class RecvWork(QObject):
|
||||
signal_received = pyqtSignal(bytes)
|
||||
|
||||
def __init__(self, socket_client: socket.socket):
|
||||
super(RecvWork, self).__init__()
|
||||
self.quit = False
|
||||
self.socket_client = socket_client
|
||||
|
||||
def work(self):
|
||||
client = self.socket_client
|
||||
client.sendall(b'long_conn')
|
||||
while True:
|
||||
try:
|
||||
recv = receive(client)
|
||||
assert recv != b''
|
||||
packets = parse_splicing_packets(recv)
|
||||
for p in packets:
|
||||
self.signal_received.emit(p)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
break
|
||||
# time.sleep(1) # 如果想验证长时间没发数据,SOCKET连接会不会断开,则可以设置时间长一点
|
||||
self.thread().quit()
|
||||
|
||||
def close_socket(self):
|
||||
self.socket_client.close()
|
||||
|
||||
|
||||
class PMClient(QObject, BaseClient):
|
||||
signal_server_message_received = pyqtSignal(dict)
|
||||
signal_data_changed = pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.client = self.init_socket(12306)
|
||||
self.thread_recv = QThread()
|
||||
self.worker_recv = RecvWork(self.client)
|
||||
self.signal_received = self.worker_recv.signal_received
|
||||
self.worker_recv.signal_received.connect(self.on_server_message_received)
|
||||
|
||||
self.worker_recv.moveToThread(self.thread_recv)
|
||||
self.thread_recv.started.connect(self.worker_recv.work)
|
||||
self.thread_recv.start()
|
||||
|
||||
def shut_down(self):
|
||||
logger.info('client quit')
|
||||
self.worker_recv.close_socket()
|
||||
self.thread_recv.quit()
|
||||
self.thread_recv.wait(500)
|
||||
|
||||
def on_server_message_received(self, packet: bytes):
|
||||
try:
|
||||
dic = json.loads(packet)
|
||||
logger.info(dic)
|
||||
msg = dic.get('message')
|
||||
if msg == 'data_changed':
|
||||
data_name = dic.get('data_name')
|
||||
if data_name is not None:
|
||||
self.signal_data_changed.emit(data_name)
|
||||
self.signal_server_message_received.emit(dic)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
def send_bytes(self, packet: bytes):
|
||||
self.client.sendall(packet)
|
||||
|
||||
@timeit
|
||||
def compress(self, byte_str):
|
||||
return zlib.compress(byte_str)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
import numpy as np
|
||||
|
||||
x = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
y = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
print(x)
|
||||
c = PMClient()
|
||||
print('set_var')
|
||||
c.set_var('x', x)
|
||||
c.set_var('y', y)
|
||||
c.get_var('x')
|
||||
print(c.get_all_var_names())
|
||||
timer = QTimer()
|
||||
timer.start(10000)
|
||||
timer.timeout.connect(lambda: c.get_var('x'))
|
||||
c.shut_down()
|
||||
sys.exit(app.exec_())
|
@ -1,85 +0,0 @@
|
||||
# coding=utf-8
|
||||
__author__ = '药师Aric'
|
||||
|
||||
import json
|
||||
import threading
|
||||
import zlib
|
||||
from typing import List
|
||||
|
||||
'''
|
||||
client端
|
||||
长连接,短连接,心跳
|
||||
'''
|
||||
import socket
|
||||
import time
|
||||
import sys
|
||||
|
||||
from pmgwidgets.communication.test.util import timeit, receive
|
||||
from pmgwidgets.communication.test.baseclient import BaseClient
|
||||
|
||||
|
||||
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
|
||||
return packet_bytes.split(b'PMEND')
|
||||
|
||||
|
||||
class GeneralClient(BaseClient):
|
||||
|
||||
def __init__(self):
|
||||
self.client = self.init_socket(12306)
|
||||
th = threading.Thread(target=self.run_event_loop)
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
|
||||
def shut_down(self):
|
||||
print('quit')
|
||||
self.client.close()
|
||||
|
||||
def on_server_message_received(self, packet: bytes):
|
||||
try:
|
||||
dic = json.loads(packet)
|
||||
msg = dic.get('message')
|
||||
if msg == 'data_changed':
|
||||
pass
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
@timeit
|
||||
def compress(self, byte_str):
|
||||
return zlib.compress(byte_str)
|
||||
|
||||
def run_event_loop(self):
|
||||
self.client.sendall(b'long_conn')
|
||||
while True:
|
||||
try:
|
||||
recv = receive(self.client)
|
||||
packets = parse_splicing_packets(recv)
|
||||
for packet in packets:
|
||||
self.on_server_message_received(packet)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import numpy as np
|
||||
|
||||
x = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
y = np.random.random(10) + np.linspace(1, 10, 10)
|
||||
print(x)
|
||||
c = GeneralClient()
|
||||
|
||||
print('set_var')
|
||||
c.set_var('x', x)
|
||||
c.set_var('y', y)
|
||||
c.set_var('z', y)
|
||||
c.set_var('w', y)
|
||||
c.set_var('t', y)
|
||||
c.set_var('y', y)
|
||||
c.get_var('x')
|
||||
print(c.get_all_vars())
|
||||
print(c.get_all_var_names())
|
||||
while (1):
|
||||
time.sleep(1)
|
@ -1,207 +0,0 @@
|
||||
"""
|
||||
基于qthread的服务器
|
||||
直接基于socket。
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import List, Tuple
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtCore import QObject, QThread, QTimer, pyqtSignal
|
||||
import socket # 导入 socket 模块
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from pmgwidgets.communication.test.util import receive, timeit
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
g_conn_pool = []
|
||||
|
||||
|
||||
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
|
||||
return packet_bytes.split(b'PMEND')
|
||||
|
||||
|
||||
class MessageWork(QObject):
|
||||
signal_message_received = pyqtSignal(socket.socket, bytes)
|
||||
signal_work_finished = pyqtSignal()
|
||||
signal_socket_closed = pyqtSignal(socket.socket)
|
||||
|
||||
def __init__(self, server: 'PMGServer'):
|
||||
super(MessageWork, self).__init__()
|
||||
self.server = server
|
||||
|
||||
def work(self):
|
||||
self.message_handle(client=self.client)
|
||||
self.signal_work_finished.emit()
|
||||
|
||||
def handle_packet(self, client: socket.socket, packet: bytes):
|
||||
try:
|
||||
dic = json.loads(packet)
|
||||
func = self.server.dispatcher_dic[dic['method']]
|
||||
args = dic['params']
|
||||
client.sendall(func(*args).encode('utf-8') + b'PMEND')
|
||||
except:
|
||||
client.close()
|
||||
self.signal_socket_closed.emit(client)
|
||||
logger.warning(repr(self.server.long_conn_sockets))
|
||||
return b''
|
||||
|
||||
def message_handle(self, client):
|
||||
"""
|
||||
消息处理
|
||||
"""
|
||||
while True:
|
||||
logger.info('client waiting!' + repr(client))
|
||||
logger.info('conn_pool_length:%d ,list is: %s' % (len(self.server.long_conn_sockets),
|
||||
repr(self.server.long_conn_sockets)))
|
||||
try:
|
||||
bytes = receive(client)
|
||||
for packet in parse_splicing_packets(bytes):
|
||||
self.handle_packet(client, packet)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
client.close()
|
||||
# 删除连接
|
||||
self.signal_socket_closed.emit(client)
|
||||
logger.info("客户端%s下线了。" % repr(client))
|
||||
break
|
||||
if len(bytes) == 0:
|
||||
client.close()
|
||||
self.signal_socket_closed.emit(client)
|
||||
logger.info("客户端%s下线了。" % repr(client))
|
||||
break
|
||||
|
||||
|
||||
class LoopWork(QObject):
|
||||
def __init__(self, server_obj: 'PMGServer', server_socket: socket.socket):
|
||||
super().__init__()
|
||||
self.server_socket = server_socket
|
||||
self.server_obj = server_obj
|
||||
self.threads = []
|
||||
|
||||
def work(self):
|
||||
self.accept_client(self.server_socket)
|
||||
|
||||
def accept_client(self, server_socket):
|
||||
"""
|
||||
接收新连接
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
client, _ = server_socket.accept() # 阻塞,等待客户端连接
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
break
|
||||
# 加入连接池
|
||||
conn_message = client.recv(1024)
|
||||
|
||||
if conn_message == b'long_conn': # 如果请求的是一个长连接,就给每个客户端创建一个独立的线程进行管理
|
||||
self.server_obj.long_conn_sockets.append(client)
|
||||
thread = QThread()
|
||||
worker = MessageWork(self.server_obj)
|
||||
worker.signal_socket_closed.connect(self.server_obj.remove_socket)
|
||||
worker.client = client
|
||||
|
||||
# worker.server = self.server_obj
|
||||
worker.moveToThread(thread)
|
||||
thread.started.connect(worker.work)
|
||||
thread.start()
|
||||
worker.signal_work_finished.connect(thread.quit)
|
||||
self.threads.append([thread, worker]) # 保持多连接要求保持引用。
|
||||
else: # 如果请求的不是一个长连接,就直接进行处理,
|
||||
|
||||
client.send(b'conn_established')
|
||||
b = receive(client)
|
||||
self.handle_packet(client, b)
|
||||
# except:
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
# client.close()
|
||||
# break
|
||||
|
||||
def handle_packet(self, client: socket.socket, packet: bytes):
|
||||
try:
|
||||
dic = json.loads(packet)
|
||||
func = self.server_obj.dispatcher_dic[dic['method']]
|
||||
args = dic['params']
|
||||
vy = func(*args).encode('utf-8') + b'PMEND'
|
||||
client.sendall(vy)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
client.close()
|
||||
|
||||
logger.warning(repr(g_conn_pool))
|
||||
return b''
|
||||
|
||||
|
||||
class PMGServer(QObject):
|
||||
def __init__(self, address=Tuple[str, int], parent=None):
|
||||
super().__init__(parent)
|
||||
self.dispatcher_dic = {}
|
||||
self.long_conn_sockets = []
|
||||
self.socket = self.init_socket(address)
|
||||
self.server_loop_thread = QThread()
|
||||
self.loop_worker = LoopWork(self, self.socket)
|
||||
self.loop_worker.moveToThread(self.server_loop_thread)
|
||||
|
||||
self.server_loop_thread.started.connect(self.loop_worker.work)
|
||||
self.server_loop_thread.start()
|
||||
|
||||
def init_socket(self, address):
|
||||
"""
|
||||
初始化套接字
|
||||
"""
|
||||
g_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 socket 对象
|
||||
|
||||
# g_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
g_socket_server.bind(address)
|
||||
g_socket_server.listen(5) # 最大等待数(有很多人理解为最大连接数,其实是错误的)
|
||||
logger.info("服务端已启动,等待客户端连接...")
|
||||
return g_socket_server
|
||||
|
||||
def broadcast_message(self, message_dic: dict = None):
|
||||
"""
|
||||
广播信息
|
||||
message:传递信息
|
||||
# 'DATA_CHANGED'
|
||||
# 'SHUT_DOWN'
|
||||
:param clients:
|
||||
:return:
|
||||
"""
|
||||
if message_dic is None:
|
||||
message_dic = {'name': 'broadcast', 'message': 'Are you alive?'}
|
||||
clients = self.long_conn_sockets
|
||||
|
||||
ids = []
|
||||
logger.info('broadcast message:'+repr(message_dic))
|
||||
for i, client in enumerate(clients):
|
||||
try:
|
||||
client.sendall(json.dumps(message_dic).encode('utf8') + b'PMEND')
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
ids.append(client)
|
||||
logger.info('died connections:' + repr(ids))
|
||||
for not_used_socket in ids:
|
||||
self.remove_socket(not_used_socket)
|
||||
|
||||
def remove_socket(self, socket_to_remove: socket.socket):
|
||||
if socket_to_remove in self.long_conn_sockets:
|
||||
self.long_conn_sockets.remove(socket_to_remove)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
ADDRESS = ('127.0.0.1', 12306) # 绑定地址
|
||||
s = PMGServer(ADDRESS)
|
||||
|
||||
qtimer = QTimer()
|
||||
qtimer.start(2000)
|
||||
qtimer.timeout.connect(lambda: s.broadcast_message(None))
|
||||
sys.exit(app.exec_())
|
@ -1,29 +0,0 @@
|
||||
import time
|
||||
import socket
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def receive(socket_obj: socket.socket) -> bytes:
|
||||
l = []
|
||||
while (1):
|
||||
try:
|
||||
b = socket_obj.recv(1024)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return b''
|
||||
l.append(b)
|
||||
if b.endswith(b'PMEND'):
|
||||
l[-1] = l[-1].strip(b'PMEND')
|
||||
break
|
||||
return b''.join(l)
|
||||
|
||||
|
||||
def timeit(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
t0 = time.time()
|
||||
r = func(*args, **kwargs)
|
||||
print('time_elapsed', time.time() - t0)
|
||||
return r
|
||||
|
||||
return wrapper
|
@ -1,22 +0,0 @@
|
||||
from PyQt5.QtWidgets import QTabWidget, QWidget
|
||||
|
||||
|
||||
class PMTabWidget(QTabWidget):
|
||||
def setup_ui(self):
|
||||
for tab_id in range(self.count()): # 遍历所有的tab
|
||||
w = self.widget(tab_id)
|
||||
if hasattr(w, 'setup_ui'):
|
||||
self.widget(tab_id).setup_ui()
|
||||
|
||||
def addScrolledAreaTab(self, widget: QWidget, a1: str) -> int:
|
||||
"""
|
||||
添加使用QScrollArea包裹的Tab。
|
||||
:param widget:
|
||||
:param a1:
|
||||
:return:
|
||||
"""
|
||||
from pmgwidgets import PMScrollArea
|
||||
scroll = PMScrollArea()
|
||||
scroll.setWidget(widget)
|
||||
|
||||
super().addTab(scroll, a1)
|
@ -1,17 +0,0 @@
|
||||
from PyQt5.QtWidgets import QDockWidget, QMainWindow
|
||||
|
||||
|
||||
class PMGDockWidget(QDockWidget):
|
||||
def __init__(self, name, text='', parent: QMainWindow = None):
|
||||
super().__init__(text, parent)
|
||||
self.parent = parent
|
||||
self.name = name
|
||||
|
||||
def raise_into_view(self):
|
||||
"""
|
||||
将控件提升到能直接看到的位置。特别适用于两个选项卡叠在一起的情况。
|
||||
:return:
|
||||
"""
|
||||
self.setVisible(True)
|
||||
self.setFocus()
|
||||
self.raise_()
|
@ -1,16 +0,0 @@
|
||||
hint = 'However if your program needn\'t this package,this warning is neglectable.'
|
||||
try:
|
||||
from .matplotlib.qt5agg import PMMatplotlibQt5Widget
|
||||
except ModuleNotFoundError:
|
||||
import warnings
|
||||
|
||||
warnings.warn('matplotlib is not installed. ' + hint)
|
||||
pass
|
||||
try:
|
||||
from .browser.browser import QWebEngineView,PMGWebBrowser,PMGWebEngineView
|
||||
except ModuleNotFoundError:
|
||||
import warnings
|
||||
|
||||
warnings.warn('QWebengine is not installed. ' + hint)
|
||||
pass
|
||||
from .editor import CythonLexer
|
@ -1,79 +0,0 @@
|
||||
"""
|
||||
代码来源:
|
||||
https://www.cnblogs.com/taostaryu/p/9772492.html
|
||||
|
||||
"""
|
||||
import sys
|
||||
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal
|
||||
from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QToolBar, QLineEdit
|
||||
|
||||
|
||||
class PMGWebBrowser(QWidget):
|
||||
def __init__(self, parent=None, toolbar='standard'):
|
||||
"""
|
||||
|
||||
:param parent:
|
||||
:param toolbar:多种选项:‘no’,‘standard’,'no_url_input'
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.webview = PMGWebEngineView()
|
||||
# self.webview.load(QUrl("https://cn.bing.com"))
|
||||
self.setLayout(QVBoxLayout())
|
||||
self.toolbar = QToolBar()
|
||||
self.url_input = QLineEdit()
|
||||
# self.url_input.setText('https://cn.bing.com')
|
||||
# self.load_url()
|
||||
self.toolbar.addWidget(self.url_input)
|
||||
self.toolbar.addAction('go').triggered.connect(lambda b: self.load_url())
|
||||
self.toolbar.addAction('back').triggered.connect(self.webview.back)
|
||||
self.toolbar.addAction('forward').triggered.connect(self.webview.forward)
|
||||
self.layout().addWidget(self.toolbar)
|
||||
if toolbar == 'no':
|
||||
self.toolbar.hide()
|
||||
elif toolbar == 'no_url_input':
|
||||
self.url_input.hide()
|
||||
|
||||
self.layout().addWidget(self.webview)
|
||||
self.setWindowTitle('My Browser')
|
||||
self.showMaximized()
|
||||
|
||||
# command:>
|
||||
# jupyter notebook --port 5000 --no-browser --ip='*' --NotebookApp.token=''
|
||||
# --NotebookApp.password='' c:\users\12957\
|
||||
|
||||
# self.webview.load(QUrl("http://127.0.0.1:5000/notebooks/desktop/Untitled.ipynb")) # 直接请求页面。
|
||||
# self.webview.load(QUrl("E:\Python\pyminer_bin\PyMiner\bin\pmgwidgets\display\browser\show_formula.html")) # 直接请求页面。
|
||||
# self.setCentralWidget(self.webview)
|
||||
|
||||
def load_url(self, url: str = ''):
|
||||
if url == '':
|
||||
url = self.url_input.text().strip()
|
||||
# print('',url)
|
||||
else:
|
||||
self.url_input.setText(url)
|
||||
self.webview.load(QUrl(url))
|
||||
|
||||
|
||||
class PMGWebEngineView(QWebEngineView):
|
||||
windowList = []
|
||||
signal_new_window_created = pyqtSignal(PMGWebBrowser)
|
||||
|
||||
# 重写createwindow()
|
||||
def createWindow(self, QWebEnginePage_WebWindowType):
|
||||
# new_webview = WebEngineView()
|
||||
new_window = PMGWebBrowser()
|
||||
# new_window.setCentralWidget(new_webview)
|
||||
# new_window.show()
|
||||
self.windowList.append(new_window) # 注:没有这句会崩溃!!!
|
||||
self.signal_new_window_created.emit(new_window)
|
||||
return new_window.webview
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
w = PMGWebBrowser()
|
||||
w.show()
|
||||
sys.exit(app.exec_())
|
@ -1,98 +0,0 @@
|
||||
# Copyright (c) Jupyter Development Team, Spyder Project Contributors.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Entry point for server rendering notebooks for Spyder."""
|
||||
|
||||
import os
|
||||
# from jinja2 import FileSystemLoader
|
||||
# from notebook.base.handlers import IPythonHandler, FileFindHandler
|
||||
from notebook.notebookapp import flags, NotebookApp
|
||||
from notebook.utils import url_path_join as ujoin
|
||||
from traitlets import Bool
|
||||
|
||||
HERE = os.path.dirname(__file__)
|
||||
|
||||
flags['dark'] = (
|
||||
{'SpyderNotebookServer': {'dark_theme': True}},
|
||||
'Use dark theme when rendering notebooks'
|
||||
)
|
||||
|
||||
|
||||
class NotebookHandler(IPythonHandler):
|
||||
"""
|
||||
Serve a notebook file from the filesystem in the notebook interface
|
||||
"""
|
||||
|
||||
# def get(self, filename):
|
||||
# """Get the main page for the application's interface."""
|
||||
# # Options set here can be read with PageConfig.getOption
|
||||
# config_data = {
|
||||
# # Use camelCase here, since that's what the lab components expect
|
||||
# 'baseUrl': self.base_url,
|
||||
# 'token': self.settings['token'],
|
||||
# 'darkTheme': self.settings['dark_theme'],
|
||||
# 'notebookPath': filename,
|
||||
# 'frontendUrl': ujoin(self.base_url, 'static/'),
|
||||
# # FIXME: Don't use a CDN here
|
||||
# 'mathjaxUrl': 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/'
|
||||
# '2.7.5/MathJax.js',
|
||||
# 'mathjaxConfig': "TeX-AMS_CHTML-full,Safe"
|
||||
# }
|
||||
# return self.write(
|
||||
# self.render_template(
|
||||
# 'index.html',
|
||||
# static=self.static_url,
|
||||
# base_url=self.base_url,
|
||||
# config_data=config_data
|
||||
# )
|
||||
# )
|
||||
#
|
||||
# def get_template(self, name):
|
||||
# loader = FileSystemLoader(HERE)
|
||||
# return loader.load(self.settings['jinja2_env'], name)
|
||||
|
||||
|
||||
class SpyderNotebookServer(NotebookApp):
|
||||
"""Server rendering notebooks in HTML and serving them over HTTP."""
|
||||
|
||||
flags = flags
|
||||
|
||||
dark_theme = Bool(
|
||||
False, config=True,
|
||||
help='Whether to use dark theme when rendering notebooks')
|
||||
|
||||
def init_webapp(self):
|
||||
"""Initialize tornado webapp and httpserver."""
|
||||
self.tornado_settings['dark_theme'] = self.dark_theme
|
||||
|
||||
super().init_webapp()
|
||||
print(self.base_url)
|
||||
url = ujoin(self.base_url, r'/notebook/(.*)')
|
||||
print(url)
|
||||
# default_handlers = [
|
||||
# (url, NotebookHandler),
|
||||
# (ujoin(self.base_url, r"/static/(.*)"), FileFindHandler,
|
||||
# {'path': os.path.join(HERE, 'build')})
|
||||
# ]
|
||||
|
||||
# self.web_app.add_handlers('.*$', default_handlers)
|
||||
|
||||
|
||||
def run():
|
||||
import time
|
||||
from IPython import get_ipython
|
||||
|
||||
while (1):
|
||||
time.sleep(2)
|
||||
_Jupyter = get_ipython()
|
||||
print(_Jupyter)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# import threading
|
||||
|
||||
# th = threading.Thread(target=run)
|
||||
# th.setDaemon(True)
|
||||
# th.start()
|
||||
|
||||
SpyderNotebookServer.launch_instance(argv=['--port', '5000', r'c:\users\12957\desktop'])
|
@ -1,12 +0,0 @@
|
||||
def register_web_browser():
|
||||
import webbrowser
|
||||
|
||||
webbrowser.register("qtbrowser", None,
|
||||
webbrowser.GenericBrowser(
|
||||
r"python E:\Python\pmgwidgetsproj\pmgwidgets\display\browser\browser.py"))
|
||||
|
||||
# c.NotebookApp.browser = 'qtbrowser'
|
||||
webbrowser.get('qtbrowser').open_new('https://cn.bing.com')
|
||||
|
||||
|
||||
register_web_browser()
|
@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MathJax TeX Test Page</title>
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({tex2jax:
|
||||
{inlineMath: [['$','$'], ['\\(','\\)']]}});
|
||||
</script>
|
||||
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>$$ TPM_{i} = \frac{ \frac{ N_{i} }{ L_{i} } * 1000000}{\sum_{i=1}^{n} \frac{N_{i}}{L_{i}}}$$
|
||||
</body>
|
||||
</html>
|
@ -1,5 +0,0 @@
|
||||
import sys
|
||||
|
||||
from pyqtgraph import examples
|
||||
|
||||
examples.run()
|
@ -1 +0,0 @@
|
||||
from .lexers.cythonlexer import CythonLexer
|
@ -1,45 +0,0 @@
|
||||
import sys
|
||||
|
||||
from PyQt5.Qsci import QsciScintilla, QsciLexerSQL, QsciLexer
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWidgets import QApplication, QStyleFactory
|
||||
|
||||
|
||||
class PMBaseEditor(QsciScintilla):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setFont(QFont('Source Code Pro', 12)) # Consolas
|
||||
|
||||
def set_lexer(self, lexer:'QsciLexer'):
|
||||
self._lexer = lexer
|
||||
self._lexer.setFont(self.font())
|
||||
self.setLexer(self._lexer)
|
||||
|
||||
# def _init_lexer(self) -> None:
|
||||
# """
|
||||
# 初始化语法解析器
|
||||
#
|
||||
# :return: None
|
||||
# """
|
||||
# self._lexer = QsciLexerSQL(self)
|
||||
# # self._lexer.setFont(self.font())
|
||||
# self.setLexer(self._lexer)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from pmgwidgets.display.editor.lexers.cythonlexer import CythonLexer
|
||||
app = QApplication(sys.argv)
|
||||
textedit = PMBaseEditor()
|
||||
textedit.setLexer(CythonLexer(textedit))
|
||||
initial_text = """
|
||||
cdef public struct Vehicle:
|
||||
int speed
|
||||
float power
|
||||
|
||||
cdef api void activate(Vehicle *v):
|
||||
if v.speed >= 88 and v.power >= 1.21:
|
||||
print("Time travel achieved")
|
||||
"""
|
||||
textedit.setText(initial_text)
|
||||
textedit.show()
|
||||
app.exec_()
|
@ -1,212 +0,0 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.Qsci import *
|
||||
import re
|
||||
|
||||
class MyLexer(QsciLexerCustom):
|
||||
def __init__(self, parent):
|
||||
super(MyLexer, self).__init__(parent)
|
||||
# Default text settings
|
||||
# ----------------------
|
||||
self.setDefaultColor(QColor("#ff000000"))
|
||||
self.setDefaultPaper(QColor("#ffffffff"))
|
||||
self.setDefaultFont(QFont("Consolas", 14))
|
||||
|
||||
# Initialize colors per style
|
||||
# ----------------------------
|
||||
self.setColor(QColor("#ff000000"), 0) # Style 0: black
|
||||
self.setColor(QColor("#ff7f0000"), 1) # Style 1: red
|
||||
self.setColor(QColor("#ff0000bf"), 2) # Style 2: blue
|
||||
self.setColor(QColor("#ff007f00"), 3) # Style 3: green
|
||||
|
||||
# Initialize paper colors per style
|
||||
# ----------------------------------
|
||||
self.setPaper(QColor("#ffffffff"), 0) # Style 0: white
|
||||
self.setPaper(QColor("#ffffffff"), 1) # Style 1: white
|
||||
self.setPaper(QColor("#ffffffff"), 2) # Style 2: white
|
||||
self.setPaper(QColor("#ffffffff"), 3) # Style 3: white
|
||||
|
||||
# Initialize fonts per style
|
||||
# ---------------------------
|
||||
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 0) # Style 0: Consolas 14pt
|
||||
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 1) # Style 1: Consolas 14pt
|
||||
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 2) # Style 2: Consolas 14pt
|
||||
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 3) # Style 3: Consolas 14pt
|
||||
|
||||
def language(self):
|
||||
return "SimpleLanguage"
|
||||
|
||||
def description(self, style):
|
||||
if style == 0:
|
||||
return "myStyle_0"
|
||||
elif style == 1:
|
||||
return "myStyle_1"
|
||||
elif style == 2:
|
||||
return "myStyle_2"
|
||||
elif style == 3:
|
||||
return "myStyle_3"
|
||||
###
|
||||
return ""
|
||||
|
||||
def styleText(self, start, end):
|
||||
# 1. Initialize the styling procedure
|
||||
# ------------------------------------
|
||||
self.startStyling(start)
|
||||
|
||||
# 2. Slice out a part from the text
|
||||
# ----------------------------------
|
||||
text = self.parent().text()[start:end]
|
||||
|
||||
# 3. Tokenize the text
|
||||
# ---------------------
|
||||
p = re.compile(r"[*]\/|\/[*]|\s+|\w+|\W")
|
||||
|
||||
# 'token_list' is a list of tuples: (token_name, token_len)
|
||||
token_list = [ (token, len(bytearray(token, "utf-8"))) for token in p.findall(text)]
|
||||
|
||||
# 4. Style the text
|
||||
# ------------------
|
||||
# 4.1 Check if multiline comment
|
||||
multiline_comm_flag = False
|
||||
editor = self.parent()
|
||||
if start > 0:
|
||||
previous_style_nr = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
|
||||
if previous_style_nr == 3:
|
||||
multiline_comm_flag = True
|
||||
# 4.2 Style the text in a loop
|
||||
for i, token in enumerate(token_list):
|
||||
if multiline_comm_flag:
|
||||
self.setStyling(token[1], 3)
|
||||
if token[0] == "*/":
|
||||
multiline_comm_flag = False
|
||||
else:
|
||||
if token[0] in ["for", "while", "return", "int", "include"]:
|
||||
# Red style
|
||||
self.setStyling(token[1], 1)
|
||||
elif token[0] in ["(", ")", "{", "}", "[", "]", "#"]:
|
||||
# Blue style
|
||||
self.setStyling(token[1], 2)
|
||||
elif token[0] == "/*":
|
||||
multiline_comm_flag = True
|
||||
self.setStyling(token[1], 3)
|
||||
else:
|
||||
# Default style
|
||||
self.setStyling(token[1], 0)
|
||||
|
||||
myCodeSample = r"""#include <stdio.h>
|
||||
/*
|
||||
* This is a
|
||||
* multiline
|
||||
* comment */
|
||||
int main()
|
||||
{
|
||||
char arr[5] = {'h', 'e', 'l', 'l', 'o'};
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 5; i++) {
|
||||
printf(arr[i]);
|
||||
}
|
||||
return 0;
|
||||
}""".replace("\n","\r\n")
|
||||
|
||||
class CEditor():
|
||||
pass
|
||||
|
||||
class CustomMainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super(CustomMainWindow, self).__init__()
|
||||
|
||||
# -------------------------------- #
|
||||
# Window setup #
|
||||
# -------------------------------- #
|
||||
|
||||
# 1. Define the geometry of the main window
|
||||
# ------------------------------------------
|
||||
self.setGeometry(300, 300, 800, 400)
|
||||
self.setWindowTitle("QScintilla Test")
|
||||
|
||||
# 2. Create frame and layout
|
||||
# ---------------------------
|
||||
self.__frm = QFrame(self)
|
||||
self.__frm.setStyleSheet("QWidget { background-color: #ffeaeaea }")
|
||||
self.__lyt = QVBoxLayout()
|
||||
self.__frm.setLayout(self.__lyt)
|
||||
self.setCentralWidget(self.__frm)
|
||||
self.__myFont = QFont()
|
||||
self.__myFont.setPointSize(14)
|
||||
|
||||
# 3. Place a button
|
||||
# ------------------
|
||||
self.__btn = QPushButton("Qsci")
|
||||
self.__btn.setFixedWidth(50)
|
||||
self.__btn.setFixedHeight(50)
|
||||
self.__btn.clicked.connect(self.__btn_action)
|
||||
self.__btn.setFont(self.__myFont)
|
||||
self.__lyt.addWidget(self.__btn)
|
||||
|
||||
# -------------------------------- #
|
||||
# QScintilla editor setup #
|
||||
# -------------------------------- #
|
||||
|
||||
# ! Make instance of QSciScintilla class!
|
||||
# ----------------------------------------
|
||||
self.__editor = QsciScintilla()
|
||||
self.__editor.setText(myCodeSample) # 'myCodeSample' is a string containing some C-code
|
||||
self.__editor.setLexer(None) # We install lexer later
|
||||
self.__editor.setUtf8(True) # Set encoding to UTF-8
|
||||
self.__editor.setFont(self.__myFont) # Gets overridden by lexer later on
|
||||
|
||||
# 1. Text wrapping
|
||||
# -----------------
|
||||
self.__editor.setWrapMode(QsciScintilla.WrapWord)
|
||||
self.__editor.setWrapVisualFlags(QsciScintilla.WrapFlagByText)
|
||||
self.__editor.setWrapIndentMode(QsciScintilla.WrapIndentIndented)
|
||||
|
||||
# 2. End-of-line mode
|
||||
# --------------------
|
||||
self.__editor.setEolMode(QsciScintilla.EolWindows)
|
||||
self.__editor.setEolVisibility(False)
|
||||
|
||||
# 3. Indentation
|
||||
# ---------------
|
||||
self.__editor.setIndentationsUseTabs(False)
|
||||
self.__editor.setTabWidth(4)
|
||||
self.__editor.setIndentationGuides(True)
|
||||
self.__editor.setTabIndents(True)
|
||||
self.__editor.setAutoIndent(True)
|
||||
|
||||
# 4. Caret
|
||||
# ---------
|
||||
self.__editor.setCaretForegroundColor(QColor("#ff0000ff"))
|
||||
self.__editor.setCaretLineVisible(True)
|
||||
self.__editor.setCaretLineBackgroundColor(QColor("#1f0000ff"))
|
||||
self.__editor.setCaretWidth(2)
|
||||
|
||||
# 5. Margins
|
||||
# -----------
|
||||
# Margin 0 = Line nr margin
|
||||
self.__editor.setMarginType(0, QsciScintilla.NumberMargin)
|
||||
self.__editor.setMarginWidth(0, "0000")
|
||||
self.__editor.setMarginsForegroundColor(QColor("#ff888888"))
|
||||
|
||||
# -------------------------------- #
|
||||
# Install lexer #
|
||||
# -------------------------------- #
|
||||
self.__lexer = MyLexer(self.__editor)
|
||||
self.__editor.setLexer(self.__lexer)
|
||||
|
||||
# ! Add editor to layout !
|
||||
# -------------------------
|
||||
self.__lyt.addWidget(self.__editor)
|
||||
self.show()
|
||||
|
||||
def __btn_action(self):
|
||||
print("Hello World!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
QApplication.setStyle(QStyleFactory.create('Fusion'))
|
||||
myGUI = CustomMainWindow()
|
||||
sys.exit(app.exec_())
|
@ -1,309 +0,0 @@
|
||||
# Import the PyQt5 module with some of the GUI widgets
|
||||
import keyword
|
||||
|
||||
import PyQt5.QtWidgets
|
||||
# Import the QScintilla module
|
||||
import PyQt5.Qsci
|
||||
# Import Python's sys module needed to get the application arguments
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
# Create a custom Nim lexer
|
||||
class CythonLexer(PyQt5.Qsci.QsciLexerCustom):
|
||||
styles = {
|
||||
"Default": 0,
|
||||
"Keyword": 1,
|
||||
"Unsafe": 2,
|
||||
"MultilineComment": 3,
|
||||
}
|
||||
keyword_list = keyword.kwlist + ['cdef', 'cpdef', 'cimport', 'enum', 'ctypedef', 'struct',
|
||||
'extern', 'api', 'public', 'private']
|
||||
unsafe_keyword_list = ['int', 'float', 'bool', 'void', 'double', 'long', 'unsigned', 'char'] +\
|
||||
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__build_class__', '__import__',
|
||||
'abs', 'all', 'any', 'ascii', 'bin', 'breakpoint', 'callable', 'chr', 'compile',
|
||||
'delattr', 'dir', 'divmod', 'eval', 'exec', 'format', 'getattr', 'globals', 'hasattr',
|
||||
'hash', 'hex', 'id', 'input', 'isinstance', 'issubclass', 'iter', 'len', 'locals',
|
||||
'max', 'min', 'next', 'oct', 'ord', 'pow', 'print',
|
||||
'repr', 'round', 'setattr', 'sorted', 'sum', 'vars',
|
||||
'None', 'Ellipsis',
|
||||
'NotImplemented',
|
||||
'False',
|
||||
'True',
|
||||
'bool',
|
||||
'memoryview',
|
||||
'bytearray',
|
||||
'bytes',
|
||||
'classmethod',
|
||||
'complex',
|
||||
'dict',
|
||||
'enumerate',
|
||||
'filter',
|
||||
'float',
|
||||
'frozenset',
|
||||
'property',
|
||||
'int',
|
||||
'list',
|
||||
'map',
|
||||
'object',
|
||||
'range',
|
||||
'reversed',
|
||||
'set',
|
||||
'slice',
|
||||
'staticmethod',
|
||||
'str',
|
||||
'super',
|
||||
'tuple',
|
||||
'type',
|
||||
'zip',
|
||||
'__debug__',
|
||||
'BaseException',
|
||||
'Exception',
|
||||
'TypeError',
|
||||
'StopAsyncIteration',
|
||||
'StopIteration',
|
||||
'GeneratorExit',
|
||||
'SystemExit',
|
||||
'KeyboardInterrupt',
|
||||
'ImportError',
|
||||
'ModuleNotFoundError',
|
||||
'OSError',
|
||||
'EnvironmentError',
|
||||
'IOError',
|
||||
'WindowsError',
|
||||
'EOFError',
|
||||
'RuntimeError',
|
||||
'RecursionError',
|
||||
'NotImplementedError',
|
||||
'NameError',
|
||||
'UnboundLocalError',
|
||||
'AttributeError',
|
||||
'SyntaxError',
|
||||
'IndentationError',
|
||||
'TabError',
|
||||
'LookupError',
|
||||
'IndexError',
|
||||
'KeyError',
|
||||
'ValueError',
|
||||
'UnicodeError',
|
||||
'UnicodeEncodeError',
|
||||
'UnicodeDecodeError',
|
||||
'UnicodeTranslateError',
|
||||
'AssertionError',
|
||||
'ArithmeticError',
|
||||
'FloatingPointError',
|
||||
'OverflowError',
|
||||
'ZeroDivisionError',
|
||||
'SystemError',
|
||||
'ReferenceError',
|
||||
'MemoryError',
|
||||
'BufferError',
|
||||
'Warning',
|
||||
'UserWarning',
|
||||
'DeprecationWarning',
|
||||
'PendingDeprecationWarning',
|
||||
'SyntaxWarning',
|
||||
'RuntimeWarning',
|
||||
'FutureWarning',
|
||||
'ImportWarning',
|
||||
'UnicodeWarning',
|
||||
'BytesWarning',
|
||||
'ResourceWarning',
|
||||
'ConnectionError',
|
||||
'BlockingIOError',
|
||||
'BrokenPipeError',
|
||||
'ChildProcessError',
|
||||
'ConnectionAbortedError',
|
||||
'ConnectionRefusedError',
|
||||
'ConnectionResetError',
|
||||
'FileExistsError',
|
||||
'FileNotFoundError',
|
||||
'IsADirectoryError',
|
||||
'NotADirectoryError',
|
||||
'InterruptedError',
|
||||
'PermissionError',
|
||||
'ProcessLookupError',
|
||||
'TimeoutError',
|
||||
'open',
|
||||
'copyright',
|
||||
'credits',
|
||||
'license',
|
||||
'help',
|
||||
'execfile',
|
||||
'runfile',
|
||||
'__IPYTHON__',
|
||||
'display',
|
||||
'get_ipython']
|
||||
|
||||
def __init__(self, parent=None):
|
||||
# Initialize superclass
|
||||
super().__init__(parent)
|
||||
assert isinstance(parent,PyQt5.Qsci.QsciScintilla)
|
||||
self.setEditor(parent)
|
||||
# Set the default style values
|
||||
self.setDefaultColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x00))
|
||||
self.setDefaultPaper(PyQt5.QtGui.QColor(0xff, 0xff, 0xff))
|
||||
self.setDefaultFont(PyQt5.QtGui.QFont("Courier", 8))
|
||||
# Initialize all style colors
|
||||
self.init_colors()
|
||||
# Init the fonts
|
||||
for i in range(len(self.styles)):
|
||||
if i == self.styles["Keyword"]:
|
||||
# Make keywords bold
|
||||
self.setFont(PyQt5.QtGui.QFont("Courier", 8, weight=PyQt5.QtGui.QFont.Black), i)
|
||||
else:
|
||||
self.setFont(PyQt5.QtGui.QFont("Courier", 8), i)
|
||||
# Set the Keywords style to be clickable with hotspots
|
||||
# using the scintilla low level messaging system
|
||||
parent.SendScintilla(
|
||||
PyQt5.Qsci.QsciScintillaBase.SCI_STYLESETHOTSPOT,
|
||||
self.styles["Keyword"],
|
||||
True
|
||||
)
|
||||
parent.SendScintilla(
|
||||
PyQt5.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEFORE,
|
||||
True,
|
||||
PyQt5.QtGui.QColor(0x00, 0x7f, 0xff)
|
||||
)
|
||||
parent.SendScintilla(
|
||||
PyQt5.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEUNDERLINE, True
|
||||
)
|
||||
|
||||
# Define a hotspot click function
|
||||
def hotspot_click(position, modifiers):
|
||||
"""
|
||||
Simple example for getting the clicked token
|
||||
"""
|
||||
text = parent.text()
|
||||
delimiters = [
|
||||
'(', ')', '[', ']', '{', '}', ' ', '.', ',', ';', '-',
|
||||
'+', '=', '/', '*', '#'
|
||||
]
|
||||
start = 0
|
||||
end = 0
|
||||
for i in range(position + 1, len(text)):
|
||||
if text[i] in delimiters:
|
||||
end = i
|
||||
break
|
||||
for i in range(position, -1, -1):
|
||||
if text[i] in delimiters:
|
||||
start = i
|
||||
break
|
||||
clicked_token = text[start:end].strip()
|
||||
# Print the token and replace it with the string "CLICK"
|
||||
print("'" + clicked_token + "'")
|
||||
parent.setSelection(0, start + 1, 0, end)
|
||||
# parent.replaceSelectedText("CLICK")
|
||||
|
||||
# Attach the hotspot click signal to a predefined function
|
||||
parent.SCN_HOTSPOTCLICK.connect(hotspot_click)
|
||||
self.cython_imported = False
|
||||
|
||||
def init_colors(self):
|
||||
# Font color
|
||||
self.setColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x00), self.styles["Default"])
|
||||
self.setColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x7f), self.styles["Keyword"])
|
||||
self.setColor(PyQt5.QtGui.QColor(0x7f, 0x00, 0x00), self.styles["Unsafe"])
|
||||
self.setColor(PyQt5.QtGui.QColor(0x7f, 0x7f, 0x00), self.styles["MultilineComment"])
|
||||
# Paper color
|
||||
for i in range(len(self.styles)):
|
||||
self.setPaper(PyQt5.QtGui.QColor(0xff, 0xff, 0xff), i)
|
||||
|
||||
def language(self):
|
||||
return "Python"
|
||||
|
||||
# def lexer(self) -> str:
|
||||
# return 'Python'
|
||||
|
||||
def description(self, style):
|
||||
if style < len(self.styles):
|
||||
description = "Custom lexer for the Nim programming languages"
|
||||
else:
|
||||
description = ""
|
||||
return description
|
||||
|
||||
def styleText(self, start, end):
|
||||
if self.cython_imported == True:
|
||||
self.cython_module.cython_style_text(start, end, self, self.parent())
|
||||
else:
|
||||
# Initialize the styling
|
||||
self.startStyling(start)
|
||||
# Tokenize the text that needs to be styled using regular expressions.
|
||||
# To style a sequence of characters you need to know the length of the sequence
|
||||
# and which style you wish to apply to the sequence. It is up to the implementer
|
||||
# to figure out which style the sequence belongs to.
|
||||
# THE PROCEDURE SHOWN BELOW IS JUST ONE OF MANY!
|
||||
splitter = re.compile(r"(\{\.|\.\}|\#|\'|\"\"\"|\n|\s+|\w+|\W)")
|
||||
# Scintilla works with bytes, so we have to adjust the start and end boundaries.
|
||||
# Like all Qt objects the lexers parent is the QScintilla editor.
|
||||
text = bytearray(self.parent().text(), "utf-8")[start:end].decode("utf-8")
|
||||
tokens = [
|
||||
(token, len(bytearray(token, "utf-8")))
|
||||
for token in splitter.findall(text)
|
||||
]
|
||||
# Multiline styles
|
||||
multiline_comment_flag = False
|
||||
# Check previous style for a multiline style
|
||||
editor = self.editor()
|
||||
if start != 0:
|
||||
previous_style = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
|
||||
if previous_style == self.styles["MultilineComment"]:
|
||||
multiline_comment_flag = True
|
||||
# Style the text in a loop
|
||||
for i, token in enumerate(tokens):
|
||||
if multiline_comment_flag == False and token[0] == "#" and tokens[i + 1][0] == "[":
|
||||
# Start of a multiline comment
|
||||
self.setStyling(token[1], self.styles["MultilineComment"])
|
||||
# Set the multiline comment flag
|
||||
multiline_comment_flag = True
|
||||
elif multiline_comment_flag == True:
|
||||
# Multiline comment flag is set
|
||||
self.setStyling(token[1], self.styles["MultilineComment"])
|
||||
# Check if a multiline comment ends
|
||||
if token[0] == "#" and tokens[i - 1][0] == "]":
|
||||
multiline_comment_flag = False
|
||||
elif token[0] in self.keyword_list:
|
||||
# Keyword
|
||||
self.setStyling(token[1], self.styles["Keyword"])
|
||||
elif token[0] in self.unsafe_keyword_list:
|
||||
# Keyword
|
||||
self.setStyling(token[1], self.styles["Unsafe"])
|
||||
else:
|
||||
# Style with the default style
|
||||
self.setStyling(token[1], self.styles["Default"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Create the main PyQt application object
|
||||
application = PyQt5.QtWidgets.QApplication(sys.argv)
|
||||
|
||||
# Create a QScintila editor instance
|
||||
|
||||
editor = PyQt5.Qsci.QsciScintilla()
|
||||
# Set the lexer to the custom Nim lexer
|
||||
nim_lexer = CythonLexer(editor)
|
||||
editor.setLexer(nim_lexer)
|
||||
# Set the initial text
|
||||
initial_text = """
|
||||
cdef public struct Vehicle:
|
||||
int speed
|
||||
float power
|
||||
|
||||
cdef api void activate(Vehicle *v):
|
||||
if v.speed >= 88 and v.power >= 1.21:
|
||||
print("Time travel achieved")
|
||||
"""
|
||||
# Set the editor's text to something huge
|
||||
editor.setText(initial_text)
|
||||
|
||||
# For the QScintilla editor to properly process events we need to add it to
|
||||
# a QMainWindow object.
|
||||
# main_window = PyQt5.QtWidgets.QMainWindow()
|
||||
# # Set the central widget of the main window to be the editor
|
||||
# main_window.setCentralWidget(editor)
|
||||
# # Resize the main window and show it on the screen
|
||||
# main_window.resize(800, 600)
|
||||
# main_window.show()
|
||||
editor.show()
|
||||
# Execute the application
|
||||
application.exec_()
|
@ -1,185 +0,0 @@
|
||||
{ontinously_data_process.json
|
||||
"nodes": {
|
||||
"1": {
|
||||
"text": "start",
|
||||
"id": "1",
|
||||
"pos": [
|
||||
-435.0,
|
||||
-90.0
|
||||
],
|
||||
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
|
||||
"content": {
|
||||
"code": "import time\ndef function(x=23):\r\n time.sleep(1)\n return [x]\n "
|
||||
},
|
||||
"input_ports": {
|
||||
"1:input:1": {
|
||||
"id": "1:input:1",
|
||||
"pos": [
|
||||
-430.0,
|
||||
-50.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "input"
|
||||
}
|
||||
},
|
||||
"output_ports": {
|
||||
"1:output:1": {
|
||||
"id": "1:output:1",
|
||||
"pos": [
|
||||
-250.0,
|
||||
-50.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "port"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"text": "x+y+z",
|
||||
"id": "3",
|
||||
"pos": [
|
||||
245.0,
|
||||
-80.0
|
||||
],
|
||||
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
|
||||
"content": {
|
||||
"code": "\nimport time\ndef function(x,y,z):\r\n time.sleep(1)\n return [x+y+z]\n "
|
||||
},
|
||||
"input_ports": {
|
||||
"3:input:1": {
|
||||
"id": "3:input:1",
|
||||
"pos": [
|
||||
250.0,
|
||||
-60.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "x"
|
||||
},
|
||||
"3:input:2": {
|
||||
"id": "3:input:2",
|
||||
"pos": [
|
||||
250.0,
|
||||
-40.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "y"
|
||||
},
|
||||
"3:input:3": {
|
||||
"id": "3:input:3",
|
||||
"pos": [
|
||||
250.0,
|
||||
-20.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "z"
|
||||
}
|
||||
},
|
||||
"output_ports": {
|
||||
"3:output:1": {
|
||||
"id": "3:output:1",
|
||||
"pos": [
|
||||
430.0,
|
||||
-40.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "output"
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"text": "process\uff1ax+2",
|
||||
"id": "4",
|
||||
"pos": [
|
||||
-100.0,
|
||||
-185.0
|
||||
],
|
||||
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
|
||||
"content": {
|
||||
"code": "\nimport time\ndef function(x):\r\n time.sleep(1)\n return [x+2]\n "
|
||||
},
|
||||
"input_ports": {
|
||||
"4:input:1": {
|
||||
"id": "4:input:1",
|
||||
"pos": [
|
||||
-95.0,
|
||||
-145.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "x"
|
||||
}
|
||||
},
|
||||
"output_ports": {
|
||||
"4:output:1": {
|
||||
"id": "4:output:1",
|
||||
"pos": [
|
||||
85.0,
|
||||
-145.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "x+2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"text": "process2:x=x*2",
|
||||
"id": "5",
|
||||
"pos": [
|
||||
-120.0,
|
||||
25.0
|
||||
],
|
||||
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
|
||||
"content": {
|
||||
"code": "\nimport time\ndef function(x):\r\n print(self)\r\n for i in range(100):\r\n self.signal_exec_doing.emit('doing')\r\n time.sleep(1)\n return [x*2]\n "
|
||||
},
|
||||
"input_ports": {
|
||||
"5:input:1": {
|
||||
"id": "5:input:1",
|
||||
"pos": [
|
||||
-115.0,
|
||||
65.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "x"
|
||||
}
|
||||
},
|
||||
"output_ports": {
|
||||
"5:output:1": {
|
||||
"id": "5:output:1",
|
||||
"pos": [
|
||||
65.0,
|
||||
65.0
|
||||
],
|
||||
"contents": {},
|
||||
"text": "x*2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"start_id": "1:output:1",
|
||||
"end_id": "4:input:1",
|
||||
"mid_positions": []
|
||||
},
|
||||
{
|
||||
"start_id": "4:output:1",
|
||||
"end_id": "3:input:1",
|
||||
"mid_positions": []
|
||||
},
|
||||
{
|
||||
"start_id": "1:output:1",
|
||||
"end_id": "5:input:1",
|
||||
"mid_positions": []
|
||||
},
|
||||
{
|
||||
"start_id": "1:output:1",
|
||||
"end_id": "3:input:2",
|
||||
"mid_positions": []
|
||||
},
|
||||
{
|
||||
"start_id": "5:output:1",
|
||||
"end_id": "3:input:3",
|
||||
"mid_positions": []
|
||||
}
|
||||
]
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
import copy
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from typing import TYPE_CHECKING, Callable, List, Union, Tuple, Dict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pmgwidgets.flowchart.flow_node import Node
|
||||
from pmgwidgets.flowchart.flowchart_widget import PMGraphicsScene
|
||||
|
||||
|
||||
class FlowContentForFunction(QObject):
|
||||
signal_exec_started = pyqtSignal(str)
|
||||
signal_exec_doing = pyqtSignal(str)
|
||||
signal_exec_finished = pyqtSignal(str)
|
||||
signal_error_occurs = pyqtSignal(str)
|
||||
|
||||
def __init__(self, node):
|
||||
super(FlowContentForFunction, self).__init__()
|
||||
self.ports_changable = [False, False]
|
||||
self.input_args = []
|
||||
|
||||
self.results = []
|
||||
self.node: 'Node' = node
|
||||
self.node.content = self
|
||||
|
||||
self.code: str = None
|
||||
self.params: List[Union[Tuple, int, float, str]] = []
|
||||
self.func: Callable = None
|
||||
self.input_port_indices: List[List] = []
|
||||
self.vars: Dict[str, Union[int, str, float, object]] = {}
|
||||
|
||||
def format_param(self) -> str:
|
||||
keys = list(self.vars.keys())
|
||||
if len(keys) == 1:
|
||||
return str(self.vars[keys[0]])
|
||||
else:
|
||||
try:
|
||||
return json.dumps(self.vars, indent=4)
|
||||
except:
|
||||
return str(self.vars)
|
||||
|
||||
def set_params(self, params: Union[str]):
|
||||
try:
|
||||
if isinstance(params, str):
|
||||
params = eval(params)
|
||||
|
||||
if isinstance(params, list):
|
||||
self.params = params
|
||||
for i in range(len(params)):
|
||||
self.params[i] = tuple(self.params[i])
|
||||
param_name = self.params[i][1]
|
||||
param_obj = self.params[i][3]
|
||||
self.vars[param_name] = param_obj
|
||||
self.node.display_internal_values(self.format_param())
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def set_function(self, function_def: str = None, func_name: str = None):
|
||||
import time
|
||||
t1 = time.time()
|
||||
self.code: str = function_def
|
||||
g = globals()
|
||||
g.update(self.vars)
|
||||
exec(self.code, g)
|
||||
self.func: Callable = globals().get(func_name)
|
||||
t2 = time.time()
|
||||
|
||||
def refresh_input_port_indices(self):
|
||||
input_port_list = [p.get_connected_port()[0] for p in self.node.input_ports if len(p.get_connected_port()) > 0]
|
||||
self.input_port_indices = []
|
||||
try:
|
||||
for p in input_port_list:
|
||||
index = p.node.get_port_index(p)
|
||||
self.input_port_indices.append([p, index])
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.signal_error_occurs.emit('')
|
||||
return
|
||||
|
||||
def get_settings_params(self):
|
||||
return self.params
|
||||
|
||||
def update_settings(self, settings_dic: dict):
|
||||
print(self.params)
|
||||
# self.ports_changable[0]= settings_dic['inputs_changeable']
|
||||
# self.ports_changable[1]= settings_dic['outputs_changeable']
|
||||
|
||||
for i, tup in enumerate(self.params):
|
||||
param_name = tup[1]
|
||||
l = list(self.params[i])
|
||||
l[3] = settings_dic[param_name]
|
||||
self.params[i] = tuple(l)
|
||||
self.vars[param_name] = settings_dic[param_name]
|
||||
self.node.display_internal_values(self.format_param())
|
||||
|
||||
def _process(self, input_args: list = None):
|
||||
if input_args is None:
|
||||
input_args = []
|
||||
try:
|
||||
for p, index in self.input_port_indices:
|
||||
val = p.node.content.results[index]
|
||||
val = copy.deepcopy(val)
|
||||
input_args.append(val)
|
||||
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.signal_error_occurs.emit('')
|
||||
return
|
||||
self.input_args = input_args
|
||||
self.signal_exec_started.emit('started')
|
||||
if not isinstance(input_args, list):
|
||||
self.signal_error_occurs.emit('input list is not instance of list')
|
||||
return None
|
||||
t1 = time.time()
|
||||
try:
|
||||
result = self.process(*input_args)
|
||||
except:
|
||||
import traceback
|
||||
exc = traceback.format_exc()
|
||||
self.signal_error_occurs.emit('Module:%s\nInfo:%s' % (self.node.text, exc))
|
||||
return None
|
||||
self.results = copy.deepcopy(result)
|
||||
next_content = self.get_next_content()
|
||||
# self.signal_exec_finished.emit(
|
||||
# 'finished\ninput value:%s\noutput value:%s' % (repr(input_list), repr(self.results)))
|
||||
self.signal_exec_finished.emit('finished')
|
||||
if next_content is None:
|
||||
return
|
||||
else:
|
||||
next_content._process()
|
||||
|
||||
def dump(self):
|
||||
return {'code': self.code, 'type': 'function', 'params': self.params, 'ports_changable': self.ports_changable}
|
||||
|
||||
def process(self, *args):
|
||||
assert self.func is not None
|
||||
globals().update(self.vars)
|
||||
return self.func(*args)
|
||||
|
||||
def get_next_content(self) -> 'FlowContentForFunction':
|
||||
scene: 'PMGraphicsScene' = self.node.base_rect.scene()
|
||||
scene.node_index_to_execute += 1
|
||||
node_index = scene.node_index_to_execute
|
||||
if node_index >= len(scene.call_id_list):
|
||||
scene.node_index_to_execute = 0
|
||||
return None
|
||||
node_id = scene.call_id_list[node_index]
|
||||
return scene.find_node(node_id).content
|
||||
|
||||
|
||||
class FlowContentEditableFunction(FlowContentForFunction):
|
||||
def __init__(self, node, code: str = ''):
|
||||
super().__init__(node)
|
||||
self.ports_changable = [True, True]
|
||||
self.input_args = []
|
||||
self.results = []
|
||||
self.node: 'Node' = node
|
||||
self.node.content = self
|
||||
if code == '':
|
||||
code = """
|
||||
import time
|
||||
def function(x,y):
|
||||
return y*2,x+2
|
||||
"""
|
||||
self.code = code
|
||||
self.set_function(code, 'function')
|
||||
|
||||
def get_settings_params(self) -> List[Union[Tuple, List]]:
|
||||
return [('text_edit', 'code', 'Input Python Code', self.code, 'python')]
|
||||
|
||||
def update_settings(self, settings_dic: dict):
|
||||
self.code = settings_dic['code']
|
||||
|
||||
def process(self, *args):
|
||||
globals().update({'self': self})
|
||||
# exec(self.code, globals())
|
||||
# return function(*args)
|
||||
|
||||
return self.func(*args)
|
||||
|
||||
def dump(self):
|
||||
return {'code': self.code, 'type': 'custom_function'}
|
||||
|
||||
|
||||
flowcontent_types = {'function': FlowContentForFunction,
|
||||
'custom_function': FlowContentEditableFunction}
|
||||
if __name__ == '__main__':
|
||||
def process():
|
||||
self = 123455
|
||||
code = """
|
||||
def function():
|
||||
print(123)
|
||||
"""
|
||||
# locals().update({'self':self})
|
||||
loc = locals()
|
||||
exec(code)
|
||||
print(loc['function'])
|
||||
|
||||
|
||||
process()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user